;; A Visitor is a structure made up of two functions ;; One for the base case and ;; one for the inductive case (define-struct Visitor (fBase fInduct)) ;; execute: list-of-any1 Visitor any2 --> any3 ;; Executes ("accepts") the visitor on the list ;; returning the result. ;; param is passed to the visitor unmodified. ;; The base case of the Visitor is called on the empty list: ;; Visitor-fBase: list-of-any1 any2 --> any3 ;; The inductive case is called on the non-empty list. ;; Visitor-fInduct: list-of-any1 any2 --> any3 (define (execute a-list visitor param) (cond [(empty? a-list) ((Visitor-fBase visitor) a-list param)] [(cons? a-list) ((Visitor-fInduct visitor) a-list param)])) ;; Sorter is a structure with two functions: ;; split splits the list into two sub-lists: ;; split: list-of-any (any any --> boolean) --> (list list-of-any list-of-any) ;; join takes two sorted sub-lists and creates a single sorted list. ;; join: (list list-of-any list-of-any) (any any --> boolean) --> list-of-any (define-struct Sorter (split join)) ;; sort: list-of-any Sorter (any any --> boolean) --> list-of-any ;; sorts the list of any in the order defined by comp. (define (sort loa sorter comp) (cond [(empty? loa) empty] [(cons? loa) (execute (rest loa) (make-Visitor (lambda (host param) (list (first loa))) (lambda (host param) ((Sorter-join sorter) (map (lambda (a-list) (sort a-list sorter comp)) ((Sorter-split sorter) loa comp)) comp))) null)])) ;; Sorting as a visitor. Note that the Sorter and the Comp functions must be bundled into a 2 element tuple. (define sortVisitor (make-Visitor (lambda (loa sorterPair) loa) (lambda (loa sorterPair) (local [(define sorter (first sorterPair)) (define comp (second sorterPair))] ((Sorter-join sorter) (map (lambda (a-list) (sort a-list sorter comp)) ((Sorter-split sorter) loa comp)) comp))))) ;; Selection sort ;; split splits off the greatest/smallest element but does not ;; disturb the position of all the other elements. ;; Join does a simple concatenation. (define selectioner (make-Sorter (lambda (a-list comp) (local [;; helper that accumulates the extrema value on the way in ;; and reassembles the list without that value on the way out. ;; fBase: returns the list of the extrema value and the empty list. ;; fInduct: cons's first back onto the recursive result if it is not ;; the extrema value. The accumulator passed to the recursive call ;;is first if it is greater/less than the supplied accumulator. (define helper (make-Visitor (lambda (host min) (list (list min) empty)) (lambda (host min) (local [(define rr (execute (rest host) helper (cond [(comp min (first host) ) (first host)] [else min])))] (cond [(equal? (first (first rr)) (first host)) rr] [else (list (first rr) (cons (first host) (second rr)))])))))] (execute a-list helper (first a-list)))) (lambda (loloa comp) (apply append loloa)))) "selectioner test cases:" (equal? empty (sort empty selectioner <)) (equal? (list 42) (sort (list 42) selectioner <)) (equal? (list 7 6 5 4 3 2 1 ) (sort (list 3 5 2 1 6 4 7) selectioner <)) (equal? empty (sort empty selectioner >)) (equal? (list 42) (sort (list 42) selectioner >)) (equal? (list 1 2 3 4 5 6 7) (sort (list 3 5 2 1 6 4 7) selectioner >)) "selectioner test cases (sortVisitor):" (equal? empty (execute empty sortVisitor (list selectioner <))) (equal? (list 42) (execute (list 42) sortVisitor (list selectioner <))) (equal? (list 7 6 5 4 3 2 1 ) (execute (list 3 5 2 1 6 4 7) sortVisitor (list selectioner <))) (equal? empty (execute empty sortVisitor (list selectioner >))) (equal? (list 42) (execute (list 42) sortVisitor (list selectioner >))) (equal? (list 1 2 3 4 5 6 7) (execute (list 3 5 2 1 6 4 7) sortVisitor (list selectioner >))) ;; Quick sort ;; split separates the list as per all the elements that are greater/less than ;; the first element (the pivot). The pivot is always at the end of ;; one of the lists. (define qsorter (make-Sorter (lambda (lon comp) (local [;; tail-recursive helper to accumulate the two lists ;; empty case returns accumulator ;; inductive case cons's first element onto one of the lists ;; in the accumulator as per its relative size w.r.t. the original ;; first. (define split_help (make-Visitor (lambda (lon_h acc1acc2) acc1acc2) (lambda (lon_h acc1acc2) (local [(define acc1 (first acc1acc2)) (define acc2 (second acc1acc2))] (cond [(comp (first lon)(first lon_h)) (execute (rest lon_h) split_help (list (cons (first lon_h) acc1) acc2))] [else (execute (rest lon_h) split_help (list acc1 (cons (first lon_h) acc2)))])))))] (execute (rest lon) split_help (list empty (list (first lon)))))) (lambda (loloa comp) (apply append loloa)))) "sort test cases, qsorter:" (equal? empty (sort empty qsorter <)) (equal? (list 42) (sort (list 42) qsorter <)) (equal? (list 7 6 5 4 3 2 1 ) (sort (list 3 5 2 1 6 4 7) qsorter <)) (equal? empty (sort empty qsorter >)) (equal? (list 42) (sort (list 42) qsorter >)) (equal? (list 1 2 3 4 5 6 7 ) (sort (list 3 5 2 1 6 4 7) qsorter >))