1 Introduction

CL-ENUMERATIONS
===============
Marco Antoniotti (c) 2004-2018
------------------------------
The directory containing this file you are reading should contain the
code and the documentation of the CL-ENUMERATIONS package.
The package is a rendition of the well known Java Enumeration/Iterator
interfaces, and it is provided as help to Java, C++, Python and other
languages programmers migrating to Common Lisp.
It is also hoped that Common Lisp vendors will adopt the interface and
provided specialized and efficient implementations of some of the
package components.
A NOTE ON FORKING
-----------------
Of course you are free to fork the project subject to the current
licensing scheme. However, before you do so, I ask you to consider
plain old "cooperation" by asking me to become a developer.
It helps keeping the entropy level at an acceptable level.
Enjoy.

5.1.1 Macros

FOREACH is a thin macro over LOOP and it mixes, in a hybrid, and maybe
not so beautiful, style the DOTIMES/DOLIST shape with LOOP clauses.

FORMS-AND-CLAUSES are executed in an environment where VAR is bound to the
elements of the result of (APPLY #’ENUM:ENUMERATE OBJECT KEYS).

If KEYS contains a non-NIL :REVERSE keyword, then the enumeration must
be bi-directional and PREVIOUS and HAS-PREVIOUS-P will be used to
traverse it. :REVERSE and its value will be still passed to the
embedded call to ENUMERATE. This is obviously only useful only for
enumerations that can start "in the middle".

FORMS-AND-CLAUSES can start with some declarations and then continue
with either regular forms or LOOP clauses. After the first LOOP
clause appears in FORMS-AND-CLAUSES, standard LOOP rules should be
followed.

FOREACH returns whatever is returned by FORMS-AND-CLAUSES according to
standard LOOP semantics.

;;; 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.

Each ENUMERATION instance maintains a reference to its "current"
element (if within "range"). Given an ENUMERATION instance
enumeration, the generic function CURRENT returns the object in such
reference.

If ERRORP is non-NIL and no "current" element is available, then
CURRENT signals an error: either NO-SUCH-ELEMENT or a continuable
error. If ERRORP is NIL and no "current" element is available then
DEFAULT is returned.

Creates a (specialized) ENUMERATION object.
If applicable START and END delimit the range of the actual enumeration.
The generic function ENUMERATE is the main entry point in the
ENUMERATIONS machinery. It takes a CL object (ENUMERABLE-ITEM) and
some additional parameters, which may or may not be used depending on
the type of the object to be enumerated.

Arguments and Values:

ENUMERABLE-ITEM : an Common Lisp object
START : an object (usually a non-negative integer)
END : an object or NIL
result : an ENUMERATION instance

Exceptional Situations:

ENUMERATE calls MAKE-INSTANCE on the appropriate ENUMERATION
sub-class. The instance initialization machinery may signal various
errors. See the documentation for each ENUMERATION sub-class for
details.

Calling ENUMERATE on a the symbol NUMBER returns a NUMBER-ENUMERATION instance.

NUMBER-ENUMERATIONs are not real enumerations per se, but they have a
nice interface and serve to render things like Python xrange type.

START must be a number, while END can be a number or NIL. The next (or
previous) element is obtained by changing the current element by BY.
BY can be a function (#’1+ is the default) or a number, in which case
it is always summed to the current element.

A NUMBER-ENUMERATION can also enumerate COMPLEX numbers, but in this
case the actual enumeration properties are completely determined by
the value of BY.

START must be either an integer between 0 and (array-total-size a), or
a LIST of indices between 0 and the appropriate limit on the
array-dimension. end must be an integer between 0 and
(array-total-size A), or a LIST of indices between 0 and the
appropriate limit on the array-dimension, or NIL. If END is NIL then
it is set to (array-total-size A).

The enumeration of a multidimensional array follows the row-major
order of Common Lisp Arrays.

Calling ENUMERATE on a HASH-TABLE returns a HASH-TABLE-ENUMERATION instance.

If KEYS is true, the HASH-TABLE-ENUMERATION scans the keys of the
underlying HASH-TABLE. If VALUES is true (the default), the
HASH-TABLE-ENUMERATION scans the values of the underlying HASH-TABLE.
If KEY-VALUE-PAIRS is true, then the HASH-TABLE-ENUMERATION yields
key-values dotted pairs.

Note that it makes no sense to set "bounds" on a
HASH-TABLE-ENUMERATION, as an HASH-TABLE is an unordered data
structure. START and END are ignored.

The generic function HAS-NEXT-P checks whether the enumeration
enumeration has another element that follows the current one in the
traversal order. If so it returns a non-NIL result, otherwise, result
is NIL.

Arguments and Values:

X : an ENUMERATION instance
result : a T.

Examples:

cl-prompt> (defvar ve (enumerate (vector 1 2 3)))
VE

cl-prompt> (has-next-p ve)
T

cl-prompt> (loop repeat 3 do (print (next ve)))
1
2
3
NIL

cl-prompt> (has-next-p ve)
NIL

Exceptional Situations:

HAS-NEXT-P signals an error (i.e., NO-APPLICABLE-METHOD is run) if X is not
an ENUMERATION instance.

The generic function NEXT returns the "next" object in the
enumeration enumeration if there is one, as determined by calling
HAS-NEXT-P. If HAS-NEXT-P returns NIL and DEFAULT is supplied, then
DEFAULT is returned. Otherwise the condition NO-SUCH-ELEMENT is
signaled.

Arguments and Values:

E : an ENUMERATION instance
DEFAULT : a T
result : a T.

Exceptional Situations:

NEXT signals the NO-SUCH-ELEMENT condition when there are no more
elements in the enumeration and no default was supplied.