diff --git a/scsh/lib/list-lib.scm b/scsh/lib/list-lib.scm index c042618..2491355 100644 --- a/scsh/lib/list-lib.scm +++ b/scsh/lib/list-lib.scm @@ -26,15 +26,19 @@ ;;; take drop ;;; take-right drop-right ;;; take! drop-right! +;;; split-at split-at! ;;; last last-pair ;;; zip unzip1 unzip2 unzip3 unzip4 unzip5 ;;; count -;;; append! append-reverse append-reverse! -;;; unfold fold fold-right pair-fold pair-fold-right reduce reduce-right +;;; append! append-reverse append-reverse! concatenate concatenate! +;;; unfold fold pair-fold reduce +;;; unfold-right fold-right pair-fold-right reduce-right ;;; append-map append-map! map! pair-for-each filter-map map-in-order ;;; filter partition remove ;;; filter! partition! remove! ;;; find find-tail any every list-index +;;; take-while drop-while take-while! +;;; span break span! break! ;;; delete delete! ;;; alist-cons alist-copy ;;; delete-duplicates delete-duplicates! @@ -246,7 +250,7 @@ ;;; (cons* a1 a2 ... an) = (cons a1 (cons a2 (cons ... an))) ;;; (cons* a1) = a1 (cons* a1 a2 ...) = (cons a1 (cons* a2 ...)) ;;; -;;; (cons first (unfold-right not-pair? car cdr rest values)) +;;; (cons first (unfold not-pair? car cdr rest values)) (define (cons* first . rest) (let recur ((x first) (rest rest)) @@ -254,7 +258,7 @@ (cons x (recur (car rest) (cdr rest))) x))) -;;; (unfold-right not-pair? car cdr lis values) +;;; (unfold not-pair? car cdr lis values) (define (list-copy lis) (let recur ((lis lis)) @@ -571,6 +575,21 @@ ; lis))) ; (list-tail lis k))) +(define (split-at x k) + (check-arg integer? k split-at) + (let recur ((lis x) (k k)) + (if (zero? k) (values '() lis) + (receive (prefix suffix) (recur (cdr lis) (- k 1)) + (values (cons (car lis) prefix) suffix))))) + +(define (split-at! x k) + (check-arg integer? k split-at!) + (if (zero? k) (values '() x) + (let* ((prev (drop x (- k 1))) + (suffix (cdr prev))) + (set-cdr! prev '()) + (values x suffix)))) + (define (last lis) (car (last-pair lis))) @@ -625,8 +644,8 @@ (cons (car (cddddr elt)) e))))))) -;;; append! append-reverse append-reverse! -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; append! append-reverse append-reverse! concatenate concatenate! +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (append! . lists) ;; First, scan through lists looking for a non-empty one. @@ -679,6 +698,8 @@ (lp next-rev rev-head))))) +(define (concatenate lists) (reduce-right append '() lists)) +(define (concatenate! lists) (reduce-right append! '() lists)) ;;; Fold/map internal utilities ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -778,25 +799,25 @@ ;;; fold/unfold ;;;;;;;;;;;;;;; -(define (unfold p f g seed . maybe-tail) - (check-arg procedure? p unfold) - (check-arg procedure? f unfold) - (check-arg procedure? g unfold) +(define (unfold-right p f g seed . maybe-tail) + (check-arg procedure? p unfold-right) + (check-arg procedure? f unfold-right) + (check-arg procedure? g unfold-right) (let lp ((seed seed) (ans (:optional maybe-tail '()))) (if (p seed) ans (lp (g seed) (cons (f seed) ans))))) -(define (unfold-right p f g seed . maybe-tail-gen) - (check-arg procedure? p unfold-right) - (check-arg procedure? f unfold-right) - (check-arg procedure? g unfold-right) +(define (unfold p f g seed . maybe-tail-gen) + (check-arg procedure? p unfold) + (check-arg procedure? f unfold) + (check-arg procedure? g unfold) (if (pair? maybe-tail-gen) (let ((tail-gen (car maybe-tail-gen))) (if (pair? (cdr maybe-tail-gen)) - (apply error "Too many arguments" unfold-right p f g seed maybe-tail-gen) + (apply error "Too many arguments" unfold p f g seed maybe-tail-gen) (let recur ((seed seed)) (if (p seed) (tail-gen seed) @@ -1252,11 +1273,8 @@ (filter! (lambda (elt) (not (= key (car elt)))) alist))) -;;; find find-tail any every list-index -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;;; ANY returns the first true value produced by PRED. -;;; FIND returns the first list elt passed by PRED. +;;; find find-tail take-while drop-while span break any every list-index +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (find pred list) (cond ((find-tail pred list) => car) @@ -1269,6 +1287,58 @@ (if (pred (car list)) list (lp (cdr list)))))) +(define (take-while pred lis) + (check-arg procedure? pred take-while) + (let recur ((lis lis)) + (if (null-list? lis) '() + (let ((x (car lis))) + (if (pred x) + (cons x (recur (cdr lis))) + '()))))) + +(define (drop-while pred lis) + (check-arg procedure? pred drop-while) + (let lp ((lis lis)) + (if (null-list? lis) '() + (if (pred (car lis)) + (lp (cdr lis)) + lis)))) + +(define (take-while! pred lis) + (check-arg procedure? pred take-while!) + (if (or (null-list? lis) (not (pred (car lis)))) '() + (begin (let lp ((prev lis) (rest (cdr lis))) + (if (pair? rest) + (let ((x (car rest))) + (if (pred x) (lp rest (cdr rest)) + (set-cdr! prev '()))))) + lis))) + +(define (span pred lis) + (check-arg procedure? pred span) + (let recur ((lis lis)) + (if (null-list? lis) (values '() '()) + (let ((x (car lis))) + (if (pred x) + (receive (prefix suffix) (recur (cdr lis)) + (values (cons x prefix) suffix)) + (values '() lis)))))) + +(define (span! pred lis) + (check-arg procedure? pred span!) + (if (or (null-list? lis) (not (pred (car lis)))) (values '() lis) + (let ((suffix (let lp ((prev lis) (rest (cdr lis))) + (if (null-list? rest) rest + (let ((x (car rest))) + (if (pred x) (lp rest (cdr rest)) + (begin (set-cdr! prev '()) + rest))))))) + (values lis suffix)))) + + +(define (break pred lis) (span (lambda (x) (not (pred x))) lis)) +(define (break! pred lis) (span! (lambda (x) (not (pred x))) lis)) + (define (any pred lis1 . lists) (check-arg procedure? pred any) (if (pair? lists) @@ -1315,8 +1385,6 @@ (if (null-list? tail) (pred head) ; Last PRED app is tail call. (and (pred head) (lp (car tail) (cdr tail)))))))) - - (define (list-index pred lis1 . lists) (check-arg procedure? pred list-index) diff --git a/scsh/lib/list-pack.scm b/scsh/lib/list-pack.scm index 6fa562a..a2f4b67 100644 --- a/scsh/lib/list-pack.scm +++ b/scsh/lib/list-pack.scm @@ -13,17 +13,21 @@ ;;; take drop ;;; take-right drop-right ;;; take! drop-right! +;;; take-while drop-while take-while! +;;; split-at split-at! +;;; span break +;;; span! break! ;;; last last-pair ;;; length+ -;;; append! reverse! append-reverse append-reverse! +;;; append! reverse! append-reverse append-reverse! concatenate concatenate! ;;; zip unzip1 unzip2 unzip3 unzip4 unzip5 ;;; count ;;; unfold unfold-right ;;; fold unfold pair-fold reduce ;;; fold-right unfold-right pair-fold-right reduce-right ;;; append-map append-map! map! pair-for-each filter-map map-in-order -;;; filter partition remove -;;; filter! partition! remove! +;;; filter partition remove +;;; filter! partition! remove! ;;; find find-tail any every list-index ;;; delete delete! delete-duplicates delete-duplicates! ;;; alist-cons alist-copy @@ -87,6 +91,9 @@ ((take drop take-right drop-right take! drop-right!) (proc (:value :exact-integer) :value)) + ((split-at split-at!) + (proc (:value :exact-integer) (some-values :value :value))) + (last (proc (:pair) :value)) (last-pair (proc (:pair) :pair)) @@ -94,6 +101,7 @@ (append! (proc (:value &rest :value) :value)) (reverse! (proc (:value) :value)) ((append-reverse append-reverse!) (proc (:value :value) :value)) + ((concatenate concatenate!) (proc (:value) :value)) (zip (proc (:value &rest :value) :value)) (unzip1 (proc (:value) :value)) @@ -138,6 +146,12 @@ ((find find-tail) (proc ((proc (:value) :boolean) :value) :value)) + ((take-while take-while! drop-while) + (proc ((proc (:value) :boolean) :value) :value)) + + ((span break span! break!) + (proc ((proc (:value) :boolean) :value) (some-values :value :value))) + ((any every) (proc ((proc (:value &rest :value) :value) :value &rest :value) :value)) diff --git a/scsh/lib/srfi-1.html b/scsh/lib/srfi-1.html index 8457db0..125ae20 100644 --- a/scsh/lib/srfi-1.html +++ b/scsh/lib/srfi-1.html @@ -253,7 +253,7 @@ implementation. I have placed this source on the Net with an unencumbered,
  • It is written for clarity and well-commented. The current source is - 706 lines of source code and 818 lines of comments and white space. + 768 lines of source code and 826 lines of comments and white space.
  • It is written for efficiency. Fast paths are provided for common cases. Side-effecting procedures such as filter! avoid unnecessary, @@ -310,15 +310,16 @@ extended R5RS take drop take-right drop-right take! drop-right! +split-at split-at! last last-pair -
    Miscellaneous: length, append, reverse, zip & count +
    Miscellaneous: length, append, concatenate, reverse, zip & count
     length length+
    -append  reverse
    -append! reverse! 
    +append  concatenate  reverse
    +append! concatenate! reverse!
     append-reverse append-reverse!
     zip unzip1 unzip2 unzip3 unzip4 unzip5
     count
    @@ -348,6 +349,8 @@ extended R5RS
     find find-tail 
     any every
     list-index
    +take-while drop-while take-while!
    +span break span! break!
     
    Deleting @@ -563,10 +566,11 @@ operators, so we don't want to exclude these possible implementations.

    The linear-update procedures in this library are

    -take! drop-right! -append! reverse! append-reverse! +take! drop-right! split-at! +append! concatenate! reverse! append-reverse! append-map! map! filter! partition! remove! +take-while! span! break! delete! alist-delete! delete-duplicates! lset-adjoin! lset-union! lset-intersection! lset-difference! lset-xor! lset-diff+intersection! @@ -1242,6 +1246,33 @@ partition the entire universe of Scheme values. + +
    + +split-at  x i -> [list object] +
    + +split-at! x i -> [list object] +
    + split-at splits the list x + at index i, returning a list of the + first i elements, and the remaining tail. It is equivalent + to +
    +(values (take x i) (drop x i))
    +
    + split-at! is the linear-update variant. It is allowed, but not + required, to alter the argument list to produce the result. +
    +(split-at '(a b c d e f g h) 3) =>
    +    (a b c)
    +    (d e f g h)
    +
    + + -

    Miscellaneous: length, append, reverse, zip & count

    +

    Miscellaneous: length, append, concatenate, reverse, zip & count

    +
    + +concatenate  list-of-lists -> value +
    + +concatenate! list-of-lists -> value +
    + These functions append the elements of their argument together. + That is, concatenate returns +
    +(apply append list-of-lists)
    +
    + or, equivalently, +
    +(reduce-right append '() list-of-lists)
    +
    + + concatenate! is the linear-update variant, defined in + terms of append! instead of append. + +

    + Note that some Scheme implementations do not support passing more than a + certain number (e.g., 64) of arguments to an n-ary procedure. + In these implementations, the (apply append ...) idiom + would fail when applied to long lists, + but concatenate would continue to function properly. + +

    + As with append and append!, + the last element of the input list may be any value at all. + @@ -1643,6 +1708,11 @@ Otherwise, return (fold f (car list) (cdr li Note: MIT Scheme and Haskell flip F's arg order for their reduce and fold functions. +

    +;; Take the max of a list of non-negative integers.
    +(reduce max 0 nums) ; i.e., (apply max 0 nums)
    +
    + @@ -1661,14 +1731,103 @@ Otherwise, return (fold f (car list) (cdr li ...in other words, we compute (fold-right f ridentity list). +
    +;; Append a bunch of lists together.
    +;; I.e., (apply append list-of-lists)
    +(reduce-right append '() list-of-lists)
    +
    +
    -unfold p f g seed [tail] -> list +unfold p f g seed [tail-gen] -> list
    - unfold constructs a list with the following loop: +unfold is best described by its basic recursion: +
    +(unfold p f g seed) = 
    +    (if (p seed) (tail-gen seed)
    +        (cons (f seed)
    +              (unfold p f g (g seed))))
    +
    +
    +
    p
    Determines when to stop unfolding. +
    f
    Maps each seed value to the corresponding list element. +
    g
    Maps each seed value to next seed value. +
    seed
    The "state" value for the unfold. +
    tail-gen
    Creates the tail of the list; + defaults to (lambda (x) '()) +
    +

    + In other words, we use g to generate a sequence of seed values +

    +seed, g(seed), g2(seed), g3(seed), ... +
    + These seed values are mapped to list elements by f, + producing the elements of the result list in a left-to-right order. + P says when to stop. + +

    + unfold is the fundamental recursive list constructor, + just as fold-right is + the fundamental recursive list consumer. + While unfold may seem a bit abstract + to novice functional programmers, it can be used in a number of ways: + +

    +;; List of squares: 1^2 ... 10^2
    +(unfold (lambda (x) (> x 10))
    +        (lambda (x) (* x x))
    +	(lambda (x) (+ x 1))
    +	1)
    +		
    +(unfold null-list? car cdr lis) ; Copy a proper list.
    +
    +;; Read current input port into a list of values.
    +(unfold eof-object? values (lambda (x) (read)) (read))
    +
    +;; Copy a possibly non-proper list:
    +(unfold not-pair? car cdr lis 
    +              values)
    +
    +;; Append HEAD onto TAIL:
    +(unfold null-list? car cdr head 
    +              (lambda (x) tail))
    +
    + + Interested functional programmers may enjoy noting that + fold-right and unfold + are in some sense inverses. + That is, given operations knull?, kar, + kdr, kons, and knil satisfying +
    +(kons (kar x) (kdr x)) = x + and +(knull? knil) = #t +
    + then +
    +(fold-right kons knil (unfold knull? kar kdr x)) = x +
    + and +
    +(unfold knull? kar kdr (fold-right kons knil x)) = x. +
    + + This combinator sometimes is called an "anamorphism;" when an + explicit tail-gen procedure is supplied, it is called an + "apomorphism." + + + +
    + +unfold-right p f g seed [tail] -> list +
    + unfold-right constructs a list with the following loop:
     (let lp ((seed seed) (lis tail))
       (if (p seed) lis
    @@ -1683,103 +1842,39 @@ Otherwise,    return (fold f (car list) (cdr li
     
    tail
    list terminator; defaults to '().

    - unfold is the fundamental iterative list constructor, + In other words, we use g to generate a sequence of seed values +

    +seed, g(seed), g2(seed), g3(seed), ... +
    + These seed values are mapped to list elements by f, + producing the elements of the result list in a right-to-left order. + P says when to stop. + +

    + unfold-right is the fundamental iterative list constructor, just as fold is the fundamental iterative list consumer. - While unfold may seem a bit abstract - to novice functional programmers, it can be used in a number of ways: -

    -;; List of squares: 1^2 ... 10^2
    -(unfold zero? 
    -	(lambda (x) (* x x))
    -	(lambda (x) (- x 1))
    -	10)
    -	
    -;; Reverse a proper list.
    -(unfold null-list? car cdr lis)
    -
    -;; Read current input port into a list of values.
    -(unfold eof-object? values (lambda (x) (read)) (read))
    -
    -;; (append-reverse rev-head tail)
    -(unfold null-list? car cdr rev-head tail)
    -
    - - Interested functional programmers may enjoy noting that - fold and unfold - are in some sense inverses. - That is, given operations knull?, kar, - kdr, kons, and knil satisfying -
    -(kons (kar x) (kdr x)) = x - and -(knull? knil) = #t -
    - then -
    -(fold kons knil (unfold knull? kar kdr x)) = x -
    - and -
    -(unfold knull? kar kdr (fold kons knil x)) = x. -
    - - This combinator presumably has some pretentious mathematical name; - interested readers are invited to communicate it to the author. - - - -
    - -unfold-right p f g seed [tail-gen] -> list -
    -unfold is best described by its basic recursion: -
    -(unfold-right p f g seed) = 
    -    (if (p seed) (tail-gen seed)
    -        (cons (f seed)
    -              (unfold-right p f g (g seed))))
    -
    -
    -
    p
    Determines when to stop unfolding. -
    f
    Maps each seed value to the corresponding list element. -
    g
    Maps each seed value to next seed value. -
    seed
    The "state" value for the unfold. -
    tail-gen
    Creates the tail of the list; - defaults to (lambda (x) '()) -
    -

    - unfold-right is the fundamental recursive list constructor, - just as fold-right is - the fundamental recursive list consumer. While unfold-right may seem a bit abstract to novice functional programmers, it can be used in a number of ways: -

     ;; List of squares: 1^2 ... 10^2
    -(unfold-right (lambda (x) (> x 10))
    +(unfold-right zero? 
                   (lambda (x) (* x x))
    -	      (lambda (x) (+ x 1))
    -	      1)
    -		
    -(unfold-right null-list? car cdr lis) ; Copy a proper list.
    +              (lambda (x) (- x 1))
    +              10)
    +	
    +;; Reverse a proper list.
    +(unfold-right null-list? car cdr lis)
     
     ;; Read current input port into a list of values.
     (unfold-right eof-object? values (lambda (x) (read)) (read))
     
    -;; Copy a possibly non-proper list:
    -(unfold-right not-pair? car cdr lis 
    -              values)
    -
    -;; Append HEAD onto TAIL:
    -(unfold-right null-list? car cdr head 
    -              (lambda (x) tail))
    +;; (append-reverse rev-head tail)
    +(unfold-right null-list? car cdr rev-head tail)
     
    Interested functional programmers may enjoy noting that - fold-right and unfold-right + fold and unfold-right are in some sense inverses. That is, given operations knull?, kar, kdr, kons, and knil satisfying @@ -1790,17 +1885,15 @@ Otherwise, return (fold f (car list) (cdr li then
    -(fold-right kons knil (unfold-right knull? kar kdr x)) = x +(fold kons knil (unfold-right knull? kar kdr x)) = x
    and
    -(unfold-right knull? kar kdr (fold-right kons knil x)) = x. +(unfold-right knull? kar kdr (fold kons knil x)) = x.
    - This combinator sometimes is called an "anamorphism;" when an - explicit tail-gen procedure is supplied, it is called an - "apomorphism." - + This combinator presumably has some pretentious mathematical name; + interested readers are invited to communicate it to the author. +
    + +take-while  pred clist -> list +
    + +take-while! pred clist -> list +
    + +Returns the longest initial prefix of clist whose elements all +satisfy the predicate pred. + +

    +Take-while! is the linear-update variant. It is allowed, but not +required, to alter the argument list to produce the result. + +

    +(take-while even? '(2 18 3 10 22 9)) => (2 18)
    +
    + + +
    + +drop-while pred clist -> list +
    +Drops the longest initial prefix of clist whose elements all +satisfy the predicate pred, and returns the rest of the list. + +
    +(drop-while even? '(2 18 3 10 22 9)) => (3 10 22 9)
    +
    +The circular-list case may be viewed as "rotating" the list. + + + +
    + +span   pred clist -> [list clist] +
    + +span!  pred list  -> [list list] +
    + +break  pred clist -> [list clist] +
    + +break! pred list  -> [list list] +
    + +Span splits the list into the longest initial prefix whose +elements all satisfy pred, and the remaining tail. +Break inverts the sense of the predicate: +the tail commences with the first element of the input list +that satisfies the predicate. + +

    +In other words: +span finds the intial span of elements +satisfying pred, +and break breaks the list at the first element satisfying +pred. + +

    +Span is equivalent to +

    +(values (take-while pred clist) 
    +        (drop-while pred clist))
    +
    + +

    +Span! and break! are the linear-update variants. +They are allowed, but not required, +to alter the argument list to produce the result. + +

    +(span even? '(2 18 3 10 22 9)) =>
    +  (2 18)
    +  (3 10 22 9)
    +
    +(break even? '(3 1 4 1 5 9)) =>
    +  (3 1)
    +  (4 1 5 9)
    +
    + + diff --git a/scsh/lib/srfi-1.txt b/scsh/lib/srfi-1.txt index 770d148..bdff052 100644 --- a/scsh/lib/srfi-1.txt +++ b/scsh/lib/srfi-1.txt @@ -1,7 +1,7 @@ The SRFI-1 list library -*- outline -*- Olin Shivers 98/10/16 -Last Update: 99/10/2 +Last Update: 99/10/3 Emacs should display this document in outline mode. Say c-h m for instructions on how to move through it by sections (e.g., c-c c-n, c-c c-p). @@ -93,7 +93,7 @@ implementation. I have placed this source on the Net with an unencumbered, - Use of a simple CHECK-ARG procedure for argument checking. - It is written for clarity and well-commented. The current source is - 706 lines of source code and 818 lines of comments and white space. + 768 lines of source code and 826 lines of comments and white space. - It is written for efficiency. Fast paths are provided for common cases. Side-effecting procedures such as FILTER! avoid unnecessary, @@ -137,13 +137,15 @@ Selectors take drop take-right drop-right take! drop-right! + split-at split-at! last last-pair -Miscellaneous: length, append, reverse, zip & count +Miscellaneous: length, append, concatenate, reverse, zip & count # length length+ # append reverse append! reverse! + concatenate concatenate! append-reverse append-reverse! zip unzip1 unzip2 unzip3 unzip4 unzip5 count @@ -157,14 +159,16 @@ Fold, unfold & map Filtering & partitioning filter partition remove - filter! partition! remove! + filter! partition! remove! Searching + member # memq memv - find find-tail + find any every list-index + take-while drop-while take-while! + span break span! break! Deleting delete delete-duplicates @@ -724,6 +728,20 @@ drop-right! flist i -> list (take! (circular-list 1 3 5) 8) => (1 3) (take! (circular-list 1 3 5) 8) => (1 3 5 1 3 5 1 3) +split-at x i -> [list object] +split-at! x i -> [list object] + SPLIT-AT splits the list X at index I, returning a list of the + first I elements, and the remaining tail. It is equivalent + to + (values (take x i) (drop x i)) + + SPLIT-AT! is the linear-update variant. It is allowed, but not + required, to alter the argument list to produce the result. + + (split-at '(a b c d e f g h) 3) => + (a b c) + (d e f g h) + last pair -> object last-pair pair -> pair LAST returns the last element of the non-empty, finite list PAIR. @@ -734,8 +752,8 @@ last-pair pair -> pair (last-pair '(a b c . d)) => (c . d) -** Miscellaneous: length, append, reverse, zip & count -====================================================== +** Miscellaneous: length, append, concatenate, reverse, zip & count +=================================================================== length list -> integer R5RS length+ clist -> integer or #f @@ -779,6 +797,25 @@ append! list1 ... -> value the result list. The last argument is never altered; the result list shares structure with this parameter. +concatenate list-of-lists -> value +concatenate! list-of-lists -> value + These functions append the elements of their argument together. + That is, CONCATENATE returns + (apply append list-of-lists) + or, equivalently, + (reduce-right append '() list-of-lists) + + CONCATENATE! is the linear-update variant, defined in + terms of APPEND! instead of APPEND. + + Note that some Scheme implementations do not support passing more than a + certain number (e.g., 64) of arguments to an n-ary procedure. In these + implementations, the (APPLY APPEND ...) idiom would fail when applied to + long lists, but CONCATENATE would continue to function properly. + + As with APPEND and APPEND!, the last element of the input list + may be any value at all. + reverse list -> list R5RS reverse! list -> list REVERSE returns a newly allocated list consisting of the elements of @@ -979,6 +1016,9 @@ reduce f ridentity list -> value Note: MIT Scheme and Haskell flip F's arg order for their REDUCE and FOLD functions. + ;; Take the max of a list of non-negative integers. + (reduce max 0 nums) ; i.e., (apply max 0 nums) + reduce-right f ridentity list -> value REDUCE-RIGHT is the fold-right variant of REDUCE. It obeys the following definition: @@ -989,8 +1029,63 @@ reduce-right f ridentity list -> value ...in other words, we compute (fold-right F RIDENTITY LIST). + ;; Append a bunch of lists together. + ;; I.e., (apply append list-of-lists) + (reduce-right append '() list-of-lists) -unfold p f g seed [tail] -> value +unfold p f g seed [tail-gen]-> list + UNFOLD is best described by its basic recursion: + (unfold p f g seed) = (if (p seed) (tail-gen seed) + (cons (f seed) + (unfold p f g (g seed)))) + P: Determines when to stop unfolding. + F: Maps each seed value to the corresponding list element. + G: Maps each seed value to next seed value. + SEED: The "state" value for the unfold. + TAIL-GEN: creates the tail of the list; defaults to (lambda (x) '()) + + In other words, we use G to generate a sequence of seed values + SEED, (G SEED), (G^2 SEED), (G^3 SEED), ... + These seed values are mapped to list elements by F, producing the + elements of the result list in a left-to-right order. P says when to stop. + + UNFOLD is the fundamental recursive list constructor, just as FOLD-RIGHT + is the fundamental recursive list consumer. While UNFOLD may seem a + bit abstract to novice functional programmers, it can be used in a number + of ways: + + (unfold (lambda (x) (> x 10)) ; List of squares: 1^2 ... 10^2. + (lambda (x) (* x x)) + (lambda (x) (+ x 1)) + 1) + + (unfold null-list? car cdr lis) ; Copy a proper list. + + ;; Read current input port into a list of values. + (unfold eof-object? values (lambda (x) (read)) (read)) + + ;; Copy a possibly non-proper list: + (unfold not-pair? car cdr lis + values) + + ;; Append HEAD onto TAIL: + (unfold null-list? car cdr head + (lambda (x) tail)) + + Interested functional programmers may enjoy noting that FOLD-RIGHT and + UNFOLD are in some sense inverses. That is, given operations KNULL?, + KAR, KDR, KONS, and KNIL satisfying + (kons (kar x) (kdr x)) = x and (knull? knil) = #t + then + (FOLD-RIGHT kons knil (UNFOLD knull? kar kdr x)) = x + and + (UNFOLD knull? kar kdr (FOLD-RIGHT kons knil x)) = x. + + This combinator sometimes is called an "anamorphism;" when an + explicit TAIL-GEN procedure is supplied, it is called an + "apomorphism." + +unfold-right p f g seed [tail] -> value UNFOLD constructs a list with the following loop: (let lp ((seed seed) (lis tail)) (if (p seed) lis @@ -1003,82 +1098,40 @@ unfold p f g seed [tail] -> value SEED: The "state" value for the unfold. TAIL: list terminator; defaults to '(). - UNFOLD is the fundamental iterative list constructor, just as FOLD is the - fundamental iterative list consumer. While UNFOLD may seem a bit abstract - to novice functional programmers, it can be used in a number of ways: + In other words, we use G to generate a sequence of seed values + SEED, (G SEED), (G^2 SEED), (G^3 SEED), ... + These seed values are mapped to list elements by F, producing the + elements of the result list in a right-to-left order. P says when to stop. - (unfold zero? ; List of squares: 1^2 ... 10^2 - (lambda (x) (* x x)) - (lambda (x) (- x 1)) - 10) - - (unfold null-list? car cdr lis) ; Reverse a proper list. + UNFOLD-RIGHT is the fundamental iterative list constructor, just as FOLD + is the fundamental iterative list consumer. While UNFOLD-RIGHT may seem a + bit abstract to novice functional programmers, it can be used in a number + of ways: - ;; Read current input port into a list of values. - (unfold eof-object? values (lambda (x) (read)) (read)) - - ;; (APPEND-REVERSE rev-head tail) - (unfold null-list? car cdr rev-head tail) - - Interested functional programmers may enjoy noting that FOLD and UNFOLD - are in some sense inverses. That is, given operations KNULL?, KAR, KDR, - KONS, and KNIL satisfying - (kons (kar x) (kdr x)) = x and (knull? knil) = #t - then - (FOLD kons knil (UNFOLD knull? kar kdr x)) = x - and - (UNFOLD knull? kar kdr (FOLD kons knil x)) = x. - - This combinator presumably has some pretentious mathematical name; - interested readers are invited to communicate it to the author. - -unfold-right p f g seed [tail-gen]-> list - UNFOLD-RIGHT is best described by its basic recursion: - (unfold-right p f g seed) = (if (p seed) (tail-gen seed) - (cons (f seed) - (unfold-right p f g (g seed)))) - P: Determines when to stop unfolding. - F: Maps each seed value to the corresponding list element. - G: Maps each seed value to next seed value. - SEED: The "state" value for the unfold. - TAIL-GEN: creates the tail of the list; defaults to (lambda (x) '()) - - UNFOLD-RIGHT is the fundamental recursive list constructor, just as - FOLD-RIGHT is the fundamental recursive list consumer. While UNFOLD-RIGHT - may seem a bit abstract to novice functional programmers, it can be used - in a number of ways: - - (unfold-right (lambda (x) (> x 10)) ; List of squares: 1^2 ... 10^2. + (unfold-right zero? ; List of squares: 1^2 ... 10^2 (lambda (x) (* x x)) - (lambda (x) (+ x 1)) - 1) + (lambda (x) (- x 1)) + 10) - (unfold-right null-list? car cdr lis) ; Copy a proper list. + (unfold-right null-list? car cdr lis) ; Reverse a proper list. ;; Read current input port into a list of values. (unfold-right eof-object? values (lambda (x) (read)) (read)) - ;; Copy a possibly non-proper list: - (unfold-right not-pair? car cdr lis - values) + ;; (APPEND-REVERSE rev-head tail) + (unfold-right null-list? car cdr rev-head tail) - ;; Append HEAD onto TAIL: - (unfold-right null-list? car cdr head - (lambda (x) tail)) - - - Interested functional programmers may enjoy noting that FOLD-RIGHT and + Interested functional programmers may enjoy noting that FOLD and UNFOLD-RIGHT are in some sense inverses. That is, given operations KNULL?, KAR, KDR, KONS, and KNIL satisfying (kons (kar x) (kdr x)) = x and (knull? knil) = #t then - (FOLD-RIGHT kons knil (UNFOLD-RIGHT knull? kar kdr x)) = x + (FOLD kons knil (UNFOLD-RIGHT knull? kar kdr x)) = x and - (UNFOLD-RIGHT knull? kar kdr (FOLD-RIGHT kons knil x)) = x. + (UNFOLD-RIGHT knull? kar kdr (FOLD kons knil x)) = x. - This combinator sometimes is called an "anamorphism;" when an - explicit TAIL-GEN procedure is supplied, it is called an - "apomorphism." + This combinator presumably has some pretentious mathematical name; + interested readers are invited to communicate it to the author. map proc clist1 clist2 ... -> list R5RS+ @@ -1337,6 +1390,55 @@ find-tail pred clist -> pair or false (find-tail (lambda (elt) (equal? x elt)) lis) In the circular-list case, this procedure "rotates" the list. + + FIND-TAIL is essentially DROP-WHILE, where the sense of the predicate + is inverted: FIND-TAIL searches until it finds an element satisfying + the predicate; DROP-WHILE searches until it finds an element that + *doesn't* satisfy the predicate. + +take-while pred clist -> list +take-while! pred clist -> list + Returns the longest initial prefix of CLIST whose elements all + satisfy the predicate PRED. + + TAKE-WHILE! is the linear-update variant. It is allowed, but not + required, to alter the argument list to produce the result. + + (take-while even? '(2 18 3 10 22 9)) => (2 18) + +drop-while pred clist -> list + Drops the longest initial prefix of LIST whose elements all + satisfy the predicate PRED, and returns the rest of the list. + + (drop-while even? '(2 18 3 10 22 9)) => (3 10 22 9) + + The circular-list case may be viewed as "rotating" the list. + +span pred clist -> [list clist] +span! pred list -> [list list] +break pred clist -> [list clist] +break! pred list -> [list list] + SPAN splits the list into the longest initial prefix whose elements + all satisfy PRED, and the remaining tail. BREAK inverts the sense + of the predicate: the tail commences with the first element of the + input list that satisfies the predicate. + + In other words: SPAN finds the intial span of elements satisfying + PRED, and BREAK breaks the list at the first element satisfying PRED. + + SPAN is equivalent to (VALUES (TAKE-WHILE PRED CLIST) + (DROP-WHILE PRED CLIST)). + + SPAN! and BREAK! are the linear-update variants. They are allowed, but not + required, to alter the argument list to produce the result. + + (span even? '(2 18 3 10 22 9)) => + (2 18) + (3 10 22 9) + + (break even? '(3 1 4 1 5 9)) => + (3 1) + (4 1 5 9) any pred clist1 clist2 ... -> value Applies the predicate across the lists, returning true if the predicate