Generators
Generators are functions that can be used to generate elements of a
sequence by calling them repetitively. Generators maintain their state
between function calls by using closures. Closures are objects that
contain pointers to both a function and a binding environment.
To understand how these work, make sure you have seen the material
on lexical scope and extent.
;;; Examples from David Touretsky and Richard Gabriel, AAAI-87 tutorial
;;; EXAMPLE 1 - GENERATORS
;;; each generator has its own copy of the variable temp
;;; the variable temp keeps the local state between function calls
;;; the generator returns a function
(defun make-generator-for-powers-of-two ()
(let ((temp 1))
;; incf increments temp by temp (temp:= temp+temp)
#'(lambda () (prog1 temp (incf temp temp)))))
;;; the symbol-value of g1 is the closure
(setf g1 (make-generator-for-powers-of-two))
;;; so we need to call it like here
(funcall g1)
(funcall g1)
;;; here the symbol-function of g1 is the closure
(setf (symbol-function 'g1) (make-generator-for-powers-of-two))
;;; so we call it as any other function
(g1)
(g1)
;;; EXAMPLE 2 - AN INCORRECT GENERATOR
;;; in this example all generators share the same copy of the variable temp.
;;; Likely this is an error, because all generators end up sharing the same
;;; state and there is no advantage in having multiple generators.
(let ((temp 1))
(defun generator-for-powers-of-two ()
#'(lambda () (prog1 temp (incf temp temp)))))
(setf f1 (generator-for-powers-of-two))
(funcall f1)
(funcall f1)
;;; EXAMPLE 3 - A MORE COMPLEX GENERATOR
;;; the leaves of a tree of conses are the atoms. This function takes
;;; as input a tree and returns a generator that enumerates the leaves of
;;; that tree. The generator returns two values: a leaf, and a flag that
;;; is T in the normal case or NIL if the generator has run out of
;;; leaves. In this last case the first value should be ignored.
(defun generate-leaves (tree)
(let ((stk (list tree)))
;; this defines a local function gen-fn
(labels ((gen-fn ()
(cond ((null stk) (values nil nil))
((atom (car stk)) (values (pop stk) t))
(t (let ((oldtop (pop stk)))
(push (cdr oldtop) stk)
(push (car oldtop) stk)
(gen-fn))))))
;; and returns it
#'gen-fn)))
(setf foo (generate-leaves '((a . b) . c)))
(funcall foo)
(funcall foo)