Overview
stateless-iterators is a library which provides Lua-like stateless
iterators for Common Lisp. Stateless iterators work as follows:
- Suppose you have a function
nextwhich takes an argumentstateand returns two values: a result of the current iteration step and a new state. Should iteration stopnextreturns a symbolstateless-iterators:stopas the new state. - Iterator is a pair of the previously described function and an
initial state:
(stateless-iterator:iterator #'next initial-state). - A function
stateless-iterators:iterate-for-effectstakes an iterator and a functionfn. It calls the iterating functionnextwith the initial state of the iterator. If iteration does not stop, the functionfnis called with the first value returned bynextas its only argument. Values returned byfnare ignored. nextis called with the second value previously returned bynextand the process repeates again until the next state is a symbolstateless-iterators:stop.
Here is a simple iterator which infinitely counts from n by 1:
(defun count-from/next (state)
(values state (1+ state)))
(defun count-from (n)
(stateles-iterators:iterator #'count-from/next n))
What's the difference with other iterator libraries (snakes being my favorite)? Iterators in this library do not contain mutable state inside themselves and so are reusable:
CL-USER> (defparameter *iter* (stateless-iterators:range 1 10))
*ITER*
CL-USER> (stateless-iterators:collect *iter*)
(1 2 3 4 5 6 7 8 9)
CL-USER> (stateless-iterators:collect *iter*)
(1 2 3 4 5 6 7 8 9)
CL-USER>
This allows to write iterators like product where one of the iterators
must be restartable:
CL-USER> (let ((a (stateless-iterators:range 0 3)))
(stateless-iterators:collect
(stateless-iterators:product a a)))
((0 . 0)(0 . 1)(0 . 2)(1 . 0)(1 . 1)(1 . 2)(2 . 0)(2 . 1)(2 . 2))
CL-USER>
NB: All functions which are passed as arguments to functions of this
library are called in unspecified order, therefore they must not have any
side-effects. One exception to this is a function passed to
iterate-for-effects. It is called right after a new value is obtained
from the iterator.