(module list mzscheme (provide set-first! first second third fourth fifth sixth seventh eighth set-rest! rest cons? empty empty? foldl foldr last-pair remv remq remove remv* remq* remove* assf memf findf filter quicksort ; deprecated mergesort ; deprecated sort sort! merge-sorted-lists merge-sorted-lists!) ;; used by sort-internal, but can be useful by itself (define (merge-sorted-lists! a b less?) (define (loop r a b r-a?) ; r-a? for optimization -- is r connected to a? (if (less? (car b) (car a)) (begin (when r-a? (set-cdr! r b)) (if (null? (cdr b)) (set-cdr! b a) (loop b a (cdr b) #f))) ;; (car a) <= (car b) (begin (unless r-a? (set-cdr! r a)) (if (null? (cdr a)) (set-cdr! a b) (loop a (cdr a) b #t))))) (cond [(null? a) b] [(null? b) a] [(less? (car b) (car a)) (if (null? (cdr b)) (set-cdr! b a) (loop b a (cdr b) #f)) b] [else ; (car a) <= (car b) (if (null? (cdr a)) (set-cdr! a b) (loop a (cdr a) b #t)) a])) ;; a non-destructive version for symmetry with merge-sorted-lists! (define (merge-sorted-lists a b less?) (cond [(null? a) b] [(null? b) a] [else (let loop ([x (car a)] [a (cdr a)] [y (car b)] [b (cdr b)]) ;; The loop handles the merging of non-empty lists. It has ;; been written this way to save testing and car/cdring. (if (less? y x) (if (null? b) (list* y x a) (cons y (loop x a (car b) (cdr b)))) ;; x <= y (if (null? a) (list* x y b) (cons x (loop (car a) (cdr a) y b)))))])) ;; 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. (define (sort-internal lst less? copy? who) (define (step n) (cond [(> 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-lists! a b less?))] ;; 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 (cdr lst)] [p2 (cddr lst)]) (let ([x (car p)] [y (car p1)] [z (car p2)]) (set! lst (cdr p2)) (cond [(less? y x) ; y x (cond [(less? z y) ; z y x (set-car! p z) (set-car! p1 y) (set-car! p2 x)] [(less? z x) ; y z x (set-car! p y) (set-car! p1 z) (set-car! p2 x)] [else ; y x z (set-car! p y) (set-car! p1 x)])] [(less? z x) ; z x y (set-car! p z) (set-car! p1 x) (set-car! p2 y)] [(less? z y) ; x z y (set-car! p1 z) (set-car! p2 y)]) (set-cdr! p2 '()) p))] [(= n 2) (let ([x (car lst)] [y (cadr lst)] [p lst]) (set! lst (cddr lst)) (when (less? y x) (set-car! p y) (set-car! (cdr p) x)) (set-cdr! (cdr p) '()) p)] [(= n 1) (let ([p lst]) (set! lst (cdr lst)) (set-cdr! p '()) p)] [else '()])) (unless (list? lst) (raise-type-error who "proper list" lst)) (unless (and (procedure? less?) (procedure-arity-includes? less? 2)) (raise-type-error who "procedure of arity 2" less?)) (let ([n (length lst)]) (cond [(<= n 1) lst] ;; check if the list is already sorted ;; (which can be a common case, eg, directory lists). [(let loop ([last (car lst)] [next (cdr lst)]) (or (null? next) (and (not (less? (car next) last)) (loop (car next) (cdr next))))) lst] [else (when copy? (set! lst (append lst '()))) (step n)]))) (define (sort! lst less?) (sort-internal lst less? #f 'sort!)) (define (sort lst less?) (sort-internal lst less? #t 'sort)) ;; deprecated! (define quicksort sort) (define mergesort sort) (define remove (letrec ([rm (case-lambda [(item list) (rm item list equal?)] [(item list equal?) (let loop ([list list]) (cond [(null? list) ()] [(equal? item (car list)) (cdr list)] [else (cons (car list) (loop (cdr list)))]))])]) rm)) (define (remq item list) (remove item list eq?)) (define (remv item list) (remove item list eqv?)) (define remove* (case-lambda [(l r equal?) (cond [(null? r) null] [else (let ([first-r (car r)]) (let loop ([l-rest l]) (cond [(null? l-rest) (cons first-r (remove* l (cdr r) equal?))] [(equal? (car l-rest) first-r) (remove* l (cdr r) equal?)] [else (loop (cdr l-rest))])))])] [(l r) (remove* l r equal?)])) (define (remq* l r) (remove* l r eq?)) (define (remv* l r) (remove* l r eqv?)) ;; fold : ((A B -> B) B (listof A) -> B) ;; fold : ((A1 ... An B -> B) B (listof A1) ... (listof An) -> B) ;; foldl builds "B" from the beginning of the list to the end of the ;; list and foldr builds the "B" from the end of the list to the ;; beginning of the list. (define (mapadd f l last) (let loop ([l l]) (if (null? l) (list last) (cons (f (car l)) (loop (cdr l)))))) (define foldl (case-lambda [(f init l) (let loop ([init init] [l l]) (if (null? l) init (loop (f (car l) init) (cdr l))))] [(f init l . ls) (let loop ([init init] [ls (cons l ls)]) (cond [(andmap pair? ls) (loop (apply f (mapadd car ls init)) (map cdr ls))] [(ormap pair? ls) (error 'foldl "received non-equal length input lists")] [else init]))])) (define foldr (case-lambda [(f init l) (let loop ([init init] [l l]) (if (null? l) init (f (car l) (loop init (cdr l)))))] [(f init l . ls) (let loop ([ls (cons l ls)]) (cond [(andmap pair? ls) (apply f (mapadd car ls (loop (map cdr ls))))] [(ormap pair? ls) (error 'foldr "received non-equal length input lists")] [else init]))])) (define-syntax (define-finder stx) (syntax-case stx () [(_ name mode) (and (identifier? #'name) (memq (syntax-e #'mode) '(assoc member find))) #`(define (name f list) (unless (and (procedure? f) (procedure-arity-includes? f 1)) (raise-type-error 'name "procedure (arity 1)" f)) (let loop ([l list]) (cond [(null? l) #f] [(not (pair? l)) (raise (make-exn:fail:contract (format "~a: second argument must be a (proper) list; given ~e" 'name list) (current-continuation-marks)))] [else (let ([a (car l)]) #,(case (syntax-e #'mode) [(member) #'(if (f a) l (loop (cdr l)))] [(find) #'(if (f a) (car l) (loop (cdr l)))] [(assoc) #'(if (pair? a) (if (f (car a)) a (loop (cdr l))) (raise-mismatch-error 'name "found a non-pair in the list: " a))]))])))])) (define-finder assf assoc) (define-finder memf member) (define-finder findf find) (define (filter f list) (unless (and (procedure? f) (procedure-arity-includes? f 1)) (raise-type-error 'filter "procedure (arity 1)" f)) ;; We use the reverse! trick because it's too easy to ;; overflow the internal stack using natural recursion. ;; It's too bad that our Scheme system is so bad, but ;; until someone fixes it... (let loop ([l list] [result null]) (cond [(null? l) (reverse! result)] [(pair? l) (loop (cdr l) (if (f (car l)) (cons (car l) result) result))] [else (raise-mismatch-error 'filter "expects a proper list: " list)]))) (define (first x) (unless (pair? x) (raise-type-error 'first "non-empty list" x)) (car x)) (define (set-first! x v) (unless (pair? x) (raise-type-error 'set-first! "non-empty list" x)) (set-car! x v)) (define-syntax define-lgetter (syntax-rules () [(_ name npos) (define (name l0) (let loop ([l l0] [pos npos]) (if (pair? l) (if (eq? pos 1) (car l) (loop (cdr l) (sub1 pos))) (raise-type-error 'name (format "list with ~a or more items" npos) l0))))])) (define-lgetter second 2) (define-lgetter third 3) (define-lgetter fourth 4) (define-lgetter fifth 5) (define-lgetter sixth 6) (define-lgetter seventh 7) (define-lgetter eighth 8) (define (rest x) (unless (pair? x) (raise-type-error 'rest "non-empty list" x)) (cdr x)) (define (set-rest! x v) (unless (pair? x) (raise-type-error 'set-rest! "non-empty list" x)) (unless (or (null? v) (pair? v)) (raise-type-error 'set-rest! "second argument must be a list" v)) (set-cdr! x v)) (define (last-pair l) (if (pair? l) (let loop ([l l] [x (cdr l)]) (if (pair? x) (loop x (cdr x)) l)) (raise-type-error 'last-pair "pair" l))) (define cons? (lambda (x) (pair? x))) (define empty? (lambda (x) (null? x))) (define empty '()))