This document contains a specification (and an implementation) for ENUMERATIONS in Common Lisp. Enumeration objects are CLOS instances which encapsulate a container data structure and maintain the state of its traversal. The ENUMERATIONS package describes a protocol (an interface) that can be implemented by other classes. Some standardized enumerations for a number of standard CL data structures are also specified.
The CL-ENUMERATIONS package is essentially a CL rendition of the
java.util.Iterator interfaces (cfr., the Java API
specification). The main goal of the specification (and of the
reference implementation) is to provide a familiar framework for Java,
C++, Python etc. programmers while they are migrating to
Common Lisp. Also, it is hoped that vendors and
implementors adopt the published API and provide efficient,
special-case, re-implementations of some of the package elements.
The ENUMERATIONS specification is a poor man SERIES package, inasmuch as it does not provide for any kind of compiler optimization.
This document tries to follow as closely as possible the typographical conventions of the CLHS.
All examples assume that the symbols exported by the
CL.EXT.DACF.ENUMERATIONS package are accessible at the
The ENUMERATIONS package consists of a set of classes and a set of supporting macros and functions. The current class hierarchy is displayed below:
The enumeration hierarchy is rooted at the ENUMERATION class. All subclasses encapsulate one particular way of stepping through a given "container".
The main distinction among the different types of enumerations lies in the nature of the underlying "enumerated object". The ENUMERATIONS package distinguishes between enumerations of things that have an explicit data structure maintained in the environment -- e.g. VECTORs, ARRAYs, etc. -- and things that have a more implicit representation, nost notably NUMBERs. This distinction makes it possible to coalesce enumerations of "containers" and enumerations of implicitly represented sets.
ENUMERATION instances are usually built by calling the ENUMERATE generic function.
cl-prompt> (setf le (enumerate '(1 2 3))) #<CONS enumeration ...>
An ENUMERATION instance can be asked whether it has more elements by calling HAS-NEXT-P.
cl-prompt> (has-next-p le) T
The NEXT generic function extracts the eponimous element from the enumeration, changing the enumeration internal state. An error of type NO-SUCH-ELEMENT is signaled if the enumeration does not have any more elements.
cl-prompt> (next le) 1
Given the state of the example, we could now write
cl-prompt> (loop while (has-next-p le) do (print (next le))) 2 3 NILwhich is the standard Java idiom.
Of course, and enumeration instance can be RESET, and a convenient macro FOREACH is available.
cl-prompt> (reset le) (1 2 3) cl-prompt> (foreach (i le) (print i)) 1 2 3 NILOf course, FOREACH is smarter than that:
cl-prompt> (foreach (i (vector 'a 's 'd)) (declare (type symbol i)) (print i)) A S D NILApart from declarations, FOREACH is just a macro built on top of LOOP, therefore you can leverage all the LOOP functionality.
cl-prompt> (foreach (i (vector 1 2 3 4)) (declare (type fixnum i)) when (evenp i) collect i) (2 4)While this creates an admittedly strange hybrid between the standard DO... operators and LOOP, it does serve the purpose. The right thing would be to have a standardized way of extending LOOP. Alas, there is no such luxury.
To conclude this example listing, consider the simple function RANGE (which is similar to Python xrange type).
cl-prompt> (foreach (i (range 0 10 3)) when (evenp i) collect i) (0 6)The function RANGE is just a shorthand for the full call to ENUMERATE.
cl-prompt> (foreach (i (enumerate 'number :start 0 :end 10 :by (lambda (cursor) (+ cursor 3)))) when (evenp i) collect i) (0 6)
The CL-ENUMERATIONS library is available from Quicklisp.
The CL-ENUMERATIONS specification and (reference) implementation can be found at common-lisp.net.
The git can be gotten from the common-lisp.net GitLab instance in the CL-ENUMERATION project page.
That's all folks. Enjoy.