Subject: Re: Generators in Lisp
From: rpw3@rpw3.org (Rob Warnock)
Date: Thu, 03 Jun 2004 20:22:36 -0500
Newsgroups: comp.lang.lisp
Message-ID: <RuednZ1c0_tBUyLdRVn-gw@speakeasy.net>
David Steuber  <david@david-steuber.com> wrote:
+---------------
| ;;; Fibonacci sequence generator as closure
| (let ((a 0) (b 1))
|   (defun fib-gen ()
|     "Return next Fibonacci number in sequence with each call"
|     (let ((ret b) (n a))
|       (setf a b 
|             b (+ ret n))
|       ret)))
| 
| (progn
|   (dotimes (i 10)
|     (format t "~A " (fib-gen)))
|   (terpri))
+---------------

That works, but of course permits only one Fibonacci sequence generator
per Lisp image. Instead, I generally prefer to use anonymous closures[1]
for such generators, which are initialized to the beginning each time
a new closure is created, and whose steppings can be interleaved in any
desired order:

    > (defun make-fib-gen (&optional (first 0) (second (1+ first)))
        (let ((a first)
              (b second))
          (flet ((fib-gen ()
                   (prog1 a
                     (psetf a b
                            b (+ a b)))))
            #'fib-gen)))

    MAKE-FIB-GEN
    > (defvar *fg1* (make-fib-gen))
    
    *FG1*
    > (defvar *fg2* (make-fib-gen))
    
    *FG2*
    > (defvar *fg3* (make-fib-gen 17 56))

    *FG3*
    > (loop for i below 10 collect (funcall *fg1*))
    
    (0 1 1 2 3 5 8 13 21 34)
    > (loop for i below 15 collect (funcall *fg2*))

    (0 1 1 2 3 5 8 13 21 34 55 89 144 233 377)
    > (loop for i below 10 collect (funcall *fg1*))

    (55 89 144 233 377 610 987 1597 2584 4181)
    >

Now let's interleave all of them [including our forgotten friend *FG3*]: 
    
    > (loop for i below 5
         collect (list (funcall *fg1*) (funcall *fg2*) (funcall *fg3*)))
         
    ((6765 610 17) (10946 987 56) (17711 1597 73) (28657 2584 129)
     (46368 4181 202)) 
    >


-Rob

[1] Although that the use of FLET as above (rather than LAMBDA per se)
    causes many implementations to still store the "name" internally
    in a way that is useful for debugging [note: this is in CMUCL,
    and I compiled MAKE-FIB-GEN between creating *FG1* & *FG2*]:

    > *fg1*

    #<Interpreted Function FIB-GEN {485717D1}>
    > *fg2*

    #<Closure Over Function FIB-GEN {48530471}>
    > 

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607