#lang racket (require test-engine/racket-gui) ; Return a sorted version of `nums` (using quicksort). ; (define (qsort1 nums) (cond [(empty? nums) empty] [else (define pivot (first nums)) (define smalls (filter ((curry >) pivot) nums)) (define equals (filter ((curry =) pivot) nums)) (define bigs (filter ((curry <) pivot) nums)) (append (qsort1 smalls) equals (qsort1 bigs))])) ; Remember, ((curry >) 3) = (λ (x) (> 3 x)) ; Return a sorted version of `nums` (using quicksort). ; A version which abstracts out the repeated filter/curry. ; (define (qsort2 nums) (cond [(empty? nums) empty] [else (match-define (list smalls equals bigs) (map (λ (op) (filter ((curry op) (first nums)) nums)) (list > = <))) (append (qsort2 smalls) equals (qsort2 bigs))])) ; Return a sorted version of `nums` (using quicksort). ; ; Another version, again abstracting out the repeated filter/curry, ; but this time not even assigning names to the three partitions. ; (This approach is complicated by the fact that we recur on the > and <, ; but *not* on the =. We do this by providing not just a comparison op to `map`, ; but also what handler to call on the filtered result.) ; (define (qsort2b nums) (if (empty? nums) empty (mappend (λ (op handler) (handler (filter ((curry op) (first nums)) nums))) (list > = <) (list qsort2b identity qsort2b)))) ; mappend: (α → β), (listof α) → (listof β) ; Like map, but append all the results. ; (define (mappend f . arglist) (apply append (apply map f arglist))) ; ; N.B. Don't confuse this function with with racket's built-in `mappend`, ; which is just append for mutable lists. ; Return a sorted version of `nums` (using quicksort). ; A version using partition, ; which makes just a *single* pass over the numbers: ; (define (qsort3 nums) (cond [(empty? nums) empty] [(cons? nums) (match-define (list smalls equals bigs) (partition (first nums) nums)) (append (qsort3 smalls) equals (qsort3 bigs))])) ; partition `nums` into those less than, equal to, and greater than `thresh`. ; We return a list of three lists. ; (N.B. This function is written accumulator-style.) ; (define (partition thresh nums) (define (help nums smalls equals bigs) (cond [(empty? nums) (list smalls equals bigs)] [(< (first nums) thresh) (help (rest nums) (cons (first nums) smalls) equals bigs)] [(= (first nums) thresh) (help (rest nums) smalls (cons (first nums) equals) bigs)] [(> (first nums) thresh) (help (rest nums) smalls equals (cons (first nums) bigs))])) (help nums '() '() '())) ; <=? : (list-of number) -> boolean ; Just like the build-in <=, expect that ; it also works when given only 0 or 1 args. ; (define (<=? . nums) (match nums [(list) #t] [(list x) #t] [_ (apply <= nums)])) (check-expect (partition 3 '(2 4 1 5 3 3 5 1)) '((1 1 2) (3 3) (5 5 4))) (check-expect (partition 0 '(2 4 1 5 3 3 5 1)) '(() () (1 5 3 3 5 1 4 2))) (check-expect (qsort1 '(4 4 4 4)) '(4 4 4 4)) (check-expect (qsort2 '(4 4 4 4)) '(4 4 4 4)) (check-expect (qsort3 '(4 4 4 4)) '(4 4 4 4)) (check-expect (qsort1 '(99 8 2 34 8 7 1 8)) '(1 2 7 8 8 8 34 99)) (check-expect (qsort2 '(99 8 2 34 8 7 1 8)) '(1 2 7 8 8 8 34 99)) (check-expect (qsort3 '(99 8 2 34 8 7 1 8)) '(1 2 7 8 8 8 34 99)) ;; Apply all three sorts to several lists, and make sure lists are ascending: ;; (for*/and {[nums '(() (2) (2 3 4) (4 3 2) (2 99 34 7 1) (99 8 2 34 8 7 1 8))] [algo (list qsort1 qsort2 qsort3)] } (apply <=? (algo nums))) (test) (printf "~nTiming results~n") (collect-garbage) (collect-garbage) (collect-garbage) (define (a-big-rand . _) (random 1000000)) (for* {[nums (map (λ (sz) (build-list sz a-big-rand)) (list 1000 10000 100000 1000000))] [algo (list qsort1 qsort2 qsort2b qsort3)] } (define-values (rslt cpu real gc) (time-apply algo (list nums))) (printf "For a list of size ~v: ~vms (includes ~vms gc)~n" (length nums) cpu gc))