on the value of already declared variables. Mutually independent variables can be declared in one with clause with an and between each declaration.
One handy feature of LOOP I haven't mentioned yet is the ability to destructure list values assigned to loop variables. This lets you take apart the value of lists that would otherwise be assigned to a loop variable, similar to the way DESTRUCTURING-BIND works but a bit less elaborate. Basically, you can replace any loop variable in a for or with clause with a tree of symbols, and the list value that would have been assigned to the simple variable will instead be destructured into variables named by the symbols in the tree. A simple example looks like this:
CL-USER> (loop for (a b) in '((1 2) (3 4) (5 6))
do (format t 'a: ~a; b: ~a~%' a b))
a: 1; b: 2
a: 3; b: 4
a: 5; b: 6
NIL
The tree can also include dotted lists, in which case the name after the dot acts like a &rest parameter, being bound to a list containing any remaining elements of the list. This is particularly handy with for/on loops since the value is always a list. For instance, this LOOP (which I used in Chapter 18 to emit a comma-delimited list):
(loop for cons on list
do (format t '~a' (car cons))
when (cdr cons) do (format t ', '))
could also be written like this:
(loop for (item . rest) on list
do (format t '~a' item)
when rest do (format t ', '))
If you want to ignore a value in the destructured list, you can use NIL in place of a variable name.
(loop for (a nil) in '((1 2) (3 4) (5 6)) collect a) ==> (1 3 5)
If the destructuring list contains more variables than there are values in the list, the extra variables are set to NIL, making all the variables essentially like &optional parameters. There isn't, however, any equivalent to &key parameters.
The value accumulation clauses are perhaps the most powerful part of LOOP. While the iteration control clauses provide a concise syntax for expressing the basic mechanics of looping, they aren't dramatically different from the equivalent mechanisms provided by DO, DOLIST, and DOTIMES.
The value accumulation clauses, on the other hand, provide a concise notation for a handful of common loop idioms having to do with accumulating values while looping. Each accumulation clause starts with a verb and follows this pattern:
Each time through the loop, an accumulation clause evaluates into subclause, the value is saved into the variable named by with clause. With no into subclause, the accumulation clause instead accumulates a default value for the whole loop expression.
The possible verbs are collect, append, nconc, count, sum, maximize, and minimize. Also available as synonyms are the present participle forms: collecting, appending, nconcing, counting, summing, maximizing, and minimizing.
A collect clause builds up a list containing all the values of LOOP does is more painful than you'd normally write by hand.[238] Related to collect are the verbs append and nconc. These verbs also accumulate values into a list, but they join the values, which must be lists, into a single list as if by the functions APPEND or NCONC.[239]
The remaining accumulation clauses are used to accumulate numeric values. The verb count counts the number of times sum collects a running total of the values of maximize collects the largest value seen for minimize collects the smallest. For instance, suppose you define a variable *random* that contains a list of random numbers.
(defparameter *random* (loop repeat 100 collect (random 10000)))
Then the following loop will return a list containing various summary information about the numbers:
(loop for i in *random*
counting (evenp i) into evens
counting (oddp i) into odds
summing i into total
maximizing i into max
minimizing i into min
finally (return (list min max total evens odds)))
As useful as the value accumulation constructs are, LOOP wouldn't be a very good general-purpose iteration facility if there wasn't a way to execute arbitrary Lisp code in the loop body.
The simplest way to execute arbitrary code within a loop body is with a do clause. Compared to the clauses I've described so far, with their prepositions and subclauses, do is a model of Yodaesque simplicity.[240] A do clause consists of the word do (or doing) followed by one or more Lisp forms that are all evaluated when the do clause is. The do clause ends at the closing parenthesis of the loop or the next loop keyword.
For instance, to print the numbers from one to ten, you could write this:
