CL Extensions: ENUMERATIONS
 

Common Lisp Extensions: ENUMERATIONS Concepts

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
  NIL
  
which 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
  NIL
  
Of course, FOREACH is smarter than that:
  cl-prompt> (foreach (i (vector 'a 's 'd))
                 (declare (type symbol i))
                 (print i))
  A
  S
  D
  NIL
  
Apart 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)