diff --git a/collects/scheme/private/sort.ss b/collects/scheme/private/sort.ss index dcc9546366..d75aae073f 100644 --- a/collects/scheme/private/sort.ss +++ b/collects/scheme/private/sort.ss @@ -4,23 +4,26 @@ (#%provide sort) -;; This is a destructive stable merge-sort, adapted from slib and improved by -;; Eli Barzilay. -;; The original source said: -;; It uses a version of merge-sort invented, to the best of my knowledge, by -;; David H. D. Warren, and first used in the DEC-10 Prolog system. -;; R. A. O'Keefe adapted it to work destructively in Scheme. -;; but it's a plain destructive merge sort, which I optimized further. +#| -;; The source uses macros to optimize some common cases (eg, no `getkey' -;; function, or precompiled versions with inlinable common comparison -;; predicates) -- they are local macros so they're not left in the compiled -;; code. +Based on "Fast mergesort implementation based on half-copying merge algorithm", +Cezary Juszczak, http://kicia.ift.uni.wroc.pl/algorytmy/mergesortpaper.pdf +Written in Scheme by Eli Barzilay. (Note: the reason for the seemingly +redundant pointer arithmetic in that paper is dealing with cases of uneven +number of elements.) + +The source uses macros to optimize some common cases (eg, no `getkey' +function, or precompiled versions with inlinable common comparison +predicates) -- they are local macros so they're not left in the compiled +code. + +Note that there is no error checking on the arguments -- the `sort' function +that this module provide is then wrapped up by a keyworded version in +"scheme/private/list.ss", and that's what everybody sees. The wrapper is +doing these checks. + +|# -;; Note that there is no error checking on the arguments -- the `sort' function -;; that this module provide is then wrapped up by a keyworded version in -;; "scheme/private/list.ss", and that's what everybody sees. The wrapper is -;; doing these checks. (define sort (let () @@ -29,80 +32,57 @@ [(dr (foo . pattern) template) (define-syntax foo (syntax-rules () [(_ . pattern) template]))])) -(define-syntax-rule (sort-internal-body lst *less? n has-getkey? getkey) +(define-syntax-rule (sort-internal-body v * n 3) - (let* (; let* not really needed with mzscheme's l->r eval - [j (quotient n 2)] [a (step j)] [b (step (- n j))]) - (merge-sorted! a b))] - ;; the following two cases are just explicit treatment of sublists - ;; of length 2 and 3, could remove both (and use the above case for - ;; n>1) and it would still work, except a little slower - [(= n 3) (let ([p lst] [p1 (mcdr lst)] [p2 (mcdr (mcdr lst))]) - (let ([x (mcar p)] [y (mcar p1)] [z (mcar p2)]) - (set! lst (mcdr p2)) - (cond [(less? y x) ; y x - (cond [(less? z y) ; z y x - (set-mcar! p z) - (set-mcar! p1 y) - (set-mcar! p2 x)] - [(less? z x) ; y z x - (set-mcar! p y) - (set-mcar! p1 z) - (set-mcar! p2 x)] - [else ; y x z - (set-mcar! p y) - (set-mcar! p1 x)])] - [(less? z x) ; z x y - (set-mcar! p z) - (set-mcar! p1 x) - (set-mcar! p2 y)] - [(less? z y) ; x z y - (set-mcar! p1 z) - (set-mcar! p2 y)]) - (set-mcdr! p2 '()) - p))] - [(= n 2) (let ([x (mcar lst)] [y (mcar (mcdr lst))] [p lst]) - (set! lst (mcdr (mcdr lst))) - (when (less? y x) - (set-mcar! p y) - (set-mcar! (mcdr p) x)) - (set-mcdr! (mcdr p) '()) - p)] - [(= n 1) (let ([p lst]) - (set! lst (mcdr lst)) - (set-mcdr! p '()) - p)] - [else '()])))) + (define-syntax-rule ( >=) @@ -112,44 +92,47 @@ (define sort-internal (case-lambda - [(less? lst n) - (let ([si (hash-ref sort-internals less? #f)]) + [( decorated-mlist - [mlst (let ([x (car lst)]) (mcons (cons (getkey x) x) null))]) - (let loop ([last mlst] [lst (cdr lst)]) + (let ([vec (make-vector (+ n (/ n 2)))]) + ;; list -> decorated-vector + (let loop ([i 0] [lst lst]) (when (pair? lst) - (let ([new (let ([x (car lst)]) (mcons (cons (getkey x) x) null))]) - (set-mcdr! last new) - (loop new (cdr lst))))) - ;; decorated-mlist -> list - (let loop ([r (sort-internal *less? mlst n car)]) - (if (null? r) r (cons (cdr (mcar r)) (loop (mcdr r))))))] + (let ([x (car lst)]) + (vector-set! vec i (cons (getkey x) x)) + (loop (add1 i) (cdr lst))))) + ;; sort + (sort-internal * list + (let loop ([i n] [r '()]) + (let ([i (sub1 i)]) + (if (< i 0) r (loop i (cons (cdr (vector-ref vec i)) r))))))] ;; trivial cases [(< n 2) lst] ;; check if the list is already sorted (which can be common, eg, ;; directory lists) [(let loop ([last (car lst)] [next (cdr lst)]) (or (null? next) - (and (not (less? (car next) last)) + (and (not ( mlist - [mlst (mcons (car lst) null)]) - (let loop ([last mlst] [lst (cdr lst)]) + (if ( vector + (let loop ([i 0] [lst lst]) (when (pair? lst) - (let ([new (mcons (car lst) null)]) - (set-mcdr! last new) - (loop new (cdr lst))))) - ;; mlist -> list - (let loop ([r (if getkey - (sort-internal *less? mlst n getkey) - (sort-internal *less? mlst n))]) - (if (null? r) r (cons (mcar r) (loop (mcdr r))))))]))) + (vector-set! vec i (car lst)) + (loop (add1 i) (cdr lst)))) + ;; sort + (if getkey + (sort-internal * list + (let loop ([i n] [r '()]) + (let ([i (sub1 i)]) + (if (< i 0) r (loop i (cons (vector-ref vec i) r))))))]))) ;; Finally, this is the provided `sort' value (case-lambda - [(lst less?) (sort-body lst less? #f #f #f)] - [(lst less? getkey) + [(lst