- iunfold p f g seed #!optional tail-genprocedure
iunfold is best described by its basic recursion:
(iunfold p f g seed) = (if (p seed) (tail-gen seed) (ipair (f seed) (iunfold p f g (g seed))))
- p
- Determines when to stop unfolding.
- f
- Maps each seed value to the corresponding ilist element.
- g
- Maps each seed value to next seed value.
- seed
- The "state" value for the unfold.
- tail-gen
- Creates the tail of the ilist; 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 ilist elements by f, producing the elements of the result ilist in a left-to-right order. P says when to stop (i.e. when it returns true the procedure ends).
iunfold is the fundamental recursive ilist constructor, just as ifold-right is the fundamental recursive ilist consumer. While iunfold may seem a bit abstract to novice functional programmers, it can be used in a number of ways:
;; Ilist of squares: 1^2 ... 10^2 (iunfold (lambda (x) (> x 10)) (lambda (x) (* x x)) (lambda (x) (+ x 1)) 1) (iunfold null-ilist? icar icdr lis) ; Copy a proper ilist. ;; Read current input port into an ilist of values. (iunfold eof-object? values (lambda (x) (read)) (read)) ;; Copy a possibly non-proper ilist: (iunfold not-ipair? icar icdr lis values) ;; Append HEAD onto TAIL: (iunfold null-ilist? icar icdr head (lambda (x) tail))
Interested functional programmers may enjoy noting that ifold-right and iunfold 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
(ifold-right kons knil (iunfold knull? kar kdr x)) ;=> x ;AND (iunfold knull? kar kdr (ifold-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."