diff --git a/pkgs/racket-doc/scribblings/reference/pairs.scrbl b/pkgs/racket-doc/scribblings/reference/pairs.scrbl index e40a2cf723..c2e579b59c 100644 --- a/pkgs/racket-doc/scribblings/reference/pairs.scrbl +++ b/pkgs/racket-doc/scribblings/reference/pairs.scrbl @@ -528,7 +528,7 @@ which takes two elements of @racket[lst] and returns a true value if the first is less (i.e., should be sorted earlier) than the second. The sort is stable; if two elements of @racket[lst] are ``equal'' -(i.e., @racket[proc] does not return a true value when given the pair in +(i.e., @racket[less-than?] does not return a true value when given the pair in either order), then the elements preserve their relative order from @racket[lst] in the output list. To preserve this guarantee, use @racket[sort] with a strict comparison functions (e.g., @racket[<] or diff --git a/pkgs/racket-doc/scribblings/reference/vectors.scrbl b/pkgs/racket-doc/scribblings/reference/vectors.scrbl index bef5ff8dbd..dd0d12994f 100644 --- a/pkgs/racket-doc/scribblings/reference/vectors.scrbl +++ b/pkgs/racket-doc/scribblings/reference/vectors.scrbl @@ -366,5 +366,56 @@ Like @racket[vector-member], but finds an element using @racket[eq?]. (vector-memq 9 (vector 1 2 3 4)) ]} +@defproc[(vector-sort [vec vector?] + [less-than? (any/c any/c . -> . any/c)] + [start exact-nonnegative-integer? 0] + [end exact-nonnegative-integer? (vector-length vec)] + [#:key key (any/c . -> . any/c) (λ (x) x)] + [#:cache-keys? cache-keys? boolean? #f]) + vector?]{ + + Like @racket[sort], but operates on vectors; a + @emph{fresh} vector of length @racket[(- end start)] is + returned containing the elements from indices + @racket[start] (inclusive) through @racket[end] (exclusive) + of @racket[vec], but in sorted order (i.e., @racket[vec] is + not modified). This sort is stable (i.e., the order of ``equal'' + elements is preserved). + +@mz-examples[#:eval vec-eval +(define v1 (vector 4 3 2 1)) +(vector-sort v1 <) +v1 +(define v2 (vector '(4) '(3) '(2) '(1))) +(vector-sort v2 < 1 3 #:key car) +v2] + +@history[#:added "6.6.0.5"]{} +} + +@defproc[(vector-sort! [vec (and/c vector? (not/c immutable?))] + [less-than? (any/c any/c . -> . any/c)] + [start exact-nonnegative-integer? 0] + [end exact-nonnegative-integer? (vector-length vec)] + [#:key key (any/c . -> . any/c) (λ (x) x)] + [#:cache-keys? cache-keys? boolean? #f]) + void?]{ + + Like @racket[vector-sort], but @emph{updates} indices + @racket[start] (inclusive) through @racket[end] (exclusive) + of @racket[vec] by sorting them according to the @racket[less-than?] + procedure. + +@mz-examples[#:eval vec-eval +(define v1 (vector 4 3 2 1)) +(vector-sort! v1 <) +v1 +(define v2 (vector '(4) '(3) '(2) '(1))) +(vector-sort! v2 < 1 3 #:key car) +v2] + +@history[#:added "6.6.0.5"]{} +} + @close-eval[vec-eval] diff --git a/pkgs/racket-test-core/tests/racket/vector.rktl b/pkgs/racket-test-core/tests/racket/vector.rktl index b5f8c744a6..5775d3ee66 100644 --- a/pkgs/racket-test-core/tests/racket/vector.rktl +++ b/pkgs/racket-test-core/tests/racket/vector.rktl @@ -229,4 +229,197 @@ (err/rt-test (vector-map (lambda (x) x) #() #() #()) (check-regs #rx"vector-map" #rx"mismatch between procedure arity"))) +;; ---------- vector-sort basic ---------- +(test #("a" "b" "c" "c" "d" "e" "f") + vector-sort + #("d" "f" "e" "c" "a" "c" "b") + string . any/c")) + (err/rt-test (vector-sort (vector 1) 1) (check-regs #rx"vector-sort" #rx"any/c any/c . -> . any/c")) + (err/rt-test (vector-sort! (vector 1) (λ (x) x)) (check-regs #rx"vector-sort!" #rx"any/c any/c . -> . any/c")) + (err/rt-test (vector-sort (vector 1) (λ (x) x)) (check-regs #rx"vector-sort" #rx"any/c any/c . -> . any/c")) + (err/rt-test (vector-sort! (vector 1) < #:key 42) (check-regs #rx"vector-sort!" #rx"any/c . -> . any/c")) + (err/rt-test (vector-sort! (vector 1) < #:key <) (check-regs #rx"vector-sort!" #rx"any/c . -> . any/c")) + (err/rt-test (vector-sort (vector 1) < #:key 42) (check-regs #rx"vector-sort" #rx"any/c . -> . any/c")) + (err/rt-test (vector-sort (vector 1) < #:key <) (check-regs #rx"vector-sort" #rx"any/c . -> . any/c"))) + +;; ---------- vector-sort! actually mutates arg, and vector-sort does not ---------- +;; verify underlying vector is sorted +(let ([v (vector 3 2 1)]) + (vector-sort! v <) + (test #t + equal? + v + (vector 1 2 3))) +(let ([v (vector "d" "f" "e" "c" "a" "c" "b")]) + (vector-sort! v stringlist v))) + (let ([c 0] [touched '()]) + ;; now cache the keys + (test sorted + vector-sort v < + #:key (λ (x) + (set! c (add1 c)) + (set! touched (cons x touched)) + (car x)) + #:cache-keys? #t) + ;; test that the number of key uses is the same as the list length + (test #t = c (vector-length v)) + ;; and that every item was touched + (test null remove* touched (vector->list v))) + (let* ([c 0] [getkey (λ (x) (set! c (add1 c)) x)]) + ;; either way, we never use the key proc on no arguments + (test #() vector-sort #() < #:key getkey #:cache-keys? #f) + (test #() vector-sort #() < #:key getkey #:cache-keys? #t) + (test #t = c 0) + ;; we also don't use it for 1-arg lists + (test #(1) vector-sort #(1) < #:key getkey #:cache-keys? #f) + (test #t = c 0) + ;; but we do use it once if caching happens (it's a consistent interface) + (test #(1) vector-sort #(1) < #:key getkey #:cache-keys? #t) + (test #t = c 1) + ;; check a few other short lists + (test #(1 2) vector-sort #(2 1) < #:key getkey #:cache-keys? #t) + (test #(1 2 3) vector-sort #(2 3 1) < #:key getkey #:cache-keys? #t) + (test #(1 2 3 4) vector-sort #(4 2 3 1) < #:key getkey #:cache-keys? #t) + (test #t = c 10))) + + (report-errs) diff --git a/racket/collects/racket/private/sort.rkt b/racket/collects/racket/private/sort.rkt index c7a66ca291..ae824ba9db 100644 --- a/racket/collects/racket/private/sort.rkt +++ b/racket/collects/racket/private/sort.rkt @@ -2,7 +2,11 @@ (#%require "small-scheme.rkt" "define.rkt" (for-syntax "stxcase-scheme.rkt")) -(#%provide sort) +;; note, these are the raw interfaces --- user-facing definitions +;; are exported from private/list.rkt and vector.rkt +(#%provide sort + vector-sort + vector-sort!) #| @@ -17,11 +21,6 @@ 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 -"racket/private/list.rkt", and that's what everybody sees. The wrapper is -doing these checks. - |# ;; This code works with unsafe operations, if there are problems, the commented @@ -31,192 +30,390 @@ doing these checks. (rename '#%unsafe i= unsafe-fx=) (rename '#%unsafe i< unsafe-fx<) (rename '#%unsafe i<= unsafe-fx<=) + (rename '#%unsafe i> unsafe-fx>) + (rename '#%unsafe i>= unsafe-fx>=) (rename '#%unsafe i>> unsafe-fxrshift) + (rename '#%unsafe i<< unsafe-fxlshift) (rename '#%unsafe vref unsafe-vector-ref) - (rename '#%unsafe vset! unsafe-vector-set!)) + (rename '#%unsafe vset! unsafe-vector-set!) + (rename '#%unsafe ucar unsafe-car) + (rename '#%unsafe ucdr unsafe-cdr) + (rename '#%unsafe unsafe-fl< unsafe-fl<) + (rename '#%unsafe unsafe-fl<= unsafe-fl<=) + (rename '#%unsafe unsafe-fl> unsafe-fl>) + (rename '#%unsafe unsafe-fl>= unsafe-fl>=)) -(define sort (let () +(define-values (sort + vector-sort + vector-sort!) +(let () -(define-syntax define-syntax-rule - (syntax-rules () - [(dr (foo . pattern) template) - (define-syntax foo (syntax-rules () [(_ . pattern) template]))])) + (define-syntax define-syntax-rule + (syntax-rules () + [(dr (foo . pattern) template) + (define-syntax foo (syntax-rules () [(_ . pattern) template]))])) + + ;; Use this to make it safe: + ;;(define-syntax-rule (i+ x y) (+ x y)) + ;;(define-syntax-rule (i- x y) (- x y)) + ;;(define-syntax-rule (i= x y) (= x y)) + ;;(define-syntax-rule (i< x y) (< x y)) + ;;(define-syntax-rule (i<= x y) (<= x y)) + ;;(define-syntax-rule (i>> x y) (arithmetic-shift x (- y))) + ;;(define-syntax-rule (i<< x y) (arithmetic-shift x y)) + ;;(define-syntax-rule (vref v i) (vector-ref v i)) + ;;(define-syntax-rule (vset! v i x) (vector-set! v i x)) + ;;(define ucar car) + ;;(define ucdr cdr) -;; Use this to make it safe: -;; (define-syntax-rule (i+ x y) (+ x y)) -;; (define-syntax-rule (i- x y) (- x y)) -;; (define-syntax-rule (i= x y) (= x y)) -;; (define-syntax-rule (i< x y) (< x y)) -;; (define-syntax-rule (i<= x y) (<= x y)) -;; (define-syntax-rule (i>> x y) (arithmetic-shift x (- y))) -;; (define-syntax-rule (vref v i) (vector-ref v i)) -;; (define-syntax-rule (vset! v i x) (vector-set! v i x)) - -(define-syntax-rule (sort-internal-body v *> n 1)] [n/2+ (i- n n/2-)]) - (define-syntax-rule (> n 1)] [n/2+ (i- n n/2-)]) - (let ([Amid1 (i+ Alo n/2-)] - [Amid2 (i+ Alo n/2+)] - [Bmid1 (i+ Blo n/2-)]) - (copying-mergesort Amid1 Bmid1 n/2+) - (copying-mergesort Alo Amid2 n/2-) - (merge #t Amid2 (i+ Alo n) Bmid1 (i+ Blo n) Blo)))])) - - (let ([Alo 0] [Amid1 n/2-] [Amid2 n/2+] [Ahi n] [B1lo n]) - (copying-mergesort Amid1 B1lo n/2+) - (unless (zero? n/2-) (copying-mergesort Alo Amid2 n/2-)) - (merge #f B1lo (i+ B1lo n/2+) Amid2 Ahi Alo)))) - -(define sort-internals (make-hasheq)) -(define _ - (let () - (define-syntax-rule (precomp >=) - (precomp string decorated-vector - (let loop ([i 0] [lst lst]) - (when (pair? lst) - (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 ( vector - (let loop ([i 0] [lst lst]) - (when (pair? lst) - (vector-set! vec i (car lst)) - (loop (add1 i) (cdr lst)))) + (define-syntax-rule (i/2 x) (i>> x 1)) + (define-syntax-rule (i*2 x) (i<< x 1)) + + (define-syntax-rule (sort-internal-body A less-than? n key) + (let () + ;; comparison & vector access macros + (define-syntax-rule ( unsafe-fl>=) + (precomp i< i<=) + (precomp i> i>=) + (precomp < <=) + (precomp > >=) + (precomp string? string>=?) + (precomp string-ci? string-ci>=?) + (precomp char? char>=?) + (precomp keyword decorated-vector + (let loop ([i 0] [lst lst]) + (when (pair? lst) + (let ([x (car lst)]) + (vset! vec i (cons (key x) x)) + (loop (i+ i 1) (cdr lst))))) + ;; sort + (sort-internal vec less-than? n #:key ucar) + ;; decorated-vector -> list + (let loop ([i n] [r '()]) + (let ([i (i- i 1)]) + (if (i< i 0) + r + (loop i (cons (ucdr (vref vec i)) r))))))] + ;; 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 ( vector + (let loop ([i 0] [lst lst]) + (when (pair? lst) + (vector-set! vec i (car lst)) + (loop (add1 i) (cdr lst)))) + ;; sort + (sort-internal vec less-than? n #:key key) + ;; vector -> list + (let loop ([i n] [r '()]) + (let ([i (sub1 i)]) + (if (< i 0) r (loop i (cons (vector-ref vec i) r))))))]))])) + + + ;; - - - - - - - - - - - - - - - - - - - - - - - - + ;; Vector Sorting Definition Body + ;; - - - - - - - - - - - - - - - - - - - - - - - - + + (define-syntax (vector-sort-body stx) + (syntax-case stx () + [(_ src-vec less-than? start end + #:key key + #:cache-keys? cache-keys? + #:constructive? constructive?) + #'(let ([n (- end start)]) + (define-syntax-rule ( decorated-vector + (let loop ([i 0]) + (when (i< i n) + (let ([x (vref src-vec (i+ i start))]) + (vset! work-vec i (cons (key x) x))) + (loop (i+ i 1)))) ;; 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))))))]))) + (sort-internal work-vec less-than? n #:key ucar) + ;; decorated-vector -> vector + (let loop ([i 0]) + (when (i< i n) + (vset! dst-vec (i+ i dst-start) (ucdr (vref work-vec i))) + (loop (i+ i 1))))] + ;; check if the vector is already sorted + [(let loop ([prev-val (vref src-vec start)] + [next-index (i+ start 1)]) + (or (i= next-index end) + (let ([next-val (vref src-vec next-index)]) + (and (not ( work-vector (to do merge sort in) + (vector-copy! work-vec 0 src-vec start end) + ;; sort! + (sort-internal work-vec less-than? n #:key key) + ;; work-vector -> dst vector + (vector-copy! dst-vec dst-start work-vec 0 n))]) + (if constructive? + dst-vec + (void)))])) + + + ;; macro for defining the various vector-sort case-lambdas + (define-syntax-rule (vector-sort-case-lambda sort-name #:constructive? constructive?) + (case-lambda + [(vec less-than? start end) + (vector-sort-body vec less-than? start end + #:key #f + #:cache-keys? #f + #:constructive? constructive?)] + [(vec less-than? start end getkey cache-keys?) + (if (and getkey (not (eq? values getkey))) + (vector-sort-body vec less-than? start end + #:key getkey + #:cache-keys? cache-keys? + #:constructive? constructive?) + (sort-name vec less-than? start end))])) + + ;; - - - - - - - - - - - - - - - - - - - - - - - - + ;; Actual Sorting Function Definitions + ;; - - - - - - - - - - - - - - - - - - - - - - - - + + ;; Finally, these are the provided `sort' values + (values + ;; sort + (case-lambda + [(lst less-than?) + (list-sort-body lst less-than? #:key #f #:cache-keys? #f)] + [(lst less-than? getkey) + (if (and getkey (not (eq? values getkey))) + (sort lst less-than? getkey #f) + (sort lst less-than?))] + [(lst less-than? getkey cache-keys?) + (if (and getkey (not (eq? values getkey))) + (list-sort-body lst less-than? #:key getkey #:cache-keys? cache-keys?) + (sort lst less-than?))]) + ;; vector-sort + (vector-sort-case-lambda vector-sort #:constructive? #t) + ;; vector-sort! + (vector-sort-case-lambda vector-sort! #:constructive? #f)) -;; Finally, this is the provided `sort' value -(case-lambda - [(lst . any/c)" less?)) + (when (and getkey (not (and (procedure? getkey) + (procedure-arity-includes? getkey 1)))) + (raise-argument-error 'name "(any/c . -> . any/c)" getkey)) + (let ([len (vector-length vec)]) + (unless (and (<= 0 start len)) + (raise-range-error 'name "vector" "starting " start vec 0 len)) + (unless (and (<= start end len)) + (raise-range-error 'name "vector" "ending " end vec start len 0))))) + + + +;; vector sort +(define (vector-sort vec less? [start 0] [end #f] + #:key [getkey #f] #:cache-keys? [cache-keys? #f]) + ;; is the input vector the right kind? (mutable vs immutable allowed?) + (unless (vector? vec) + (raise-argument-error 'vector-sort "vector?" vec)) + + ;; calulate end if not provided + (let ([end (or end (vector-length vec))]) + (perform-common-sort-arg-checks vector-sort vec less? start end getkey) + (if getkey + (raw-vector-sort vec less? start end getkey cache-keys?) + (raw-vector-sort vec less? start end)))) + +;; vector sort +(define (vector-sort! vec less? [start 0] [end #f] + #:key [getkey #f] + #:cache-keys? [cache-keys? #f]) + ;; is the input vector the right kind? (mutable vs immutable allowed?) + (unless (and (vector? vec) (not (immutable? vec))) + (raise-argument-error 'vector-sort! + "(and/c vector? (not/c immutable?))" + vec)) + + ;; calulate end if not provided + (let ([end (or end (vector-length vec))]) + (perform-common-sort-arg-checks vector-sort! vec less? start end getkey) + (if getkey + (raw-vector-sort! vec less? start end getkey cache-keys?) + (raw-vector-sort! vec less? start end))))