In principle, drivers can be implemented just as easily as other
iterate clauses. In practice, they are a little harder to get right.
As an example, consider writing a driver that iterates over all the
elements of a vector, ignoring its fill-pointer.
in-vector won't work for this, because it observes the fill-pointer.
It's necessary to use
array-dimension instead of
to obtain the size of the vector. Here is one approach:
(defmacro-clause (FOR var IN-WHOLE-VECTOR v) "All the elements of a vector (disregards fill-pointer)" (let ((vect (gensym)) (index (gensym))) `(progn (with ,vect = ,v) (for ,index from 0 below (array-dimension ,vect 0)) (for ,var = (aref ,vect ,index)))))
Note that we immediately put
v in a variable, in case it is an
expression. Again, this is just good Lisp macrology. It also has a
subtle effect on the semantics of the driver:
v is evaluated
only once, at the beginning of the loop, so changes to
v in the
loop have no effect on the driver. Similarly, the bounds for
numerical iteration e.g. the above
array-dimension are also
evaluated once only. This is how all of
iterate's drivers work.
There is an important point concerning the
progn in this code.
We need the
progn, of course, because we are returning several
forms, one of which is a driver. But
iterate drivers must occur at
top-level. Is this code in error? No, because top-level is
iterate to include forms inside a
progn. This is
just the definition of top-level that Common Lisp uses, and for the
same reason: to allow macros to return multiple forms at top-level.
for... in-whole-vector clause will work, it is
not ideal. In particular, it does not support generating. Do do so,
we need to use
for... next or
The job is simplified by the
Defines a driver clause in both the
generateforms, and provides a parameter
generatewhich body can examine to determine how it was invoked. arglist is as in
defmacro-clause, and should begin with the symbol
defmacro-driver, our driver looks like this:
(defmacro-driver (FOR var IN-WHOLE-VECTOR v) "All the elements of a vector (disregards fill-pointer)" (let ((vect (gensym)) (end (gensym)) (index (gensym)) (kwd (if generate 'generate 'for))) `(progn (with ,vect = ,v) (with ,end = (array-dimension ,vect 0)) (with ,index = -1) (,kwd ,var next (progn (incf ,index) (if (>= ,index ,end) (terminate)) (aref ,vect ,index))))))
We are still missing one thing: the
We can get them easily enough, by writing
(defmacro-driver (FOR var IN-WHOLE-VECTOR v &sequence) ...)
We can now refer to parameters
etc. which contain either the values for the corresponding keyword, or
nil if the keyword was not supplied. Implementing the right
code for these keywords is cumbersome but not difficult; it is left as
an exercise. But before you begin, see
below for an easier way.