loop is a macro with two forms. The simple form, (loop form*), where every form is a list, repeats its forms forever inside a nil block, so (return) ends it. The extended form starts with a symbol, and then its arguments are read as a small language made of loop keywords and forms. The keywords are recognised by their name (the package does not matter, so :for and for are the same), and only in the places where the grammar expects one. This is why the viewer colours a loop word only directly inside a loop, and not when it follows for, as, with, into or named, where a variable name stands.
Grammar
loop [named name] {variable-clause}* {main-clause}*
variable-clause ::= with-clause | initial-final | for-as-clause
main-clause ::= unconditional | accumulation | conditional
| termination-test | initial-final
with-clause ::= with var [type-spec] [= form] {and var [type-spec] [= form]}*
initial-final ::= initially compound-form+ | finally compound-form+
for-as-clause ::= {for | as} for-as-subclause {and for-as-subclause}*
unconditional ::= {do | doing} compound-form+ | return {form | it}
accumulation ::= {collect | collecting | append | appending | nconc | nconcing}
{form | it} [into var]
| {count | counting | sum | summing | maximize | maximizing
| minimize | minimizing} {form | it} [into var] [type-spec]
conditional ::= {if | when | unless} form selectable-clause {and selectable-clause}*
[else selectable-clause {and selectable-clause}*] [end]
termination-test ::= while form | until form | repeat form
| always form | never form | thereis form
type-spec ::= simple-type | of-type d-type-spec
Variable clauses come before main clauses in the standard; implementations accept a for after a while or a do but SBCL and others warn, since the effect is undefined in the standard.
Every loop keyword
| Keyword | Role | Meaning |
|---|---|---|
named | name clause | names the loop’s block, for (return-from name …); must come first |
with | variable | an auxiliary variable, bound once; and binds in parallel, otherwise in sequence |
for, as | variable | an iteration variable (the two words mean the same); the subclauses are below |
initially | prologue | forms run once before the first iteration, after the bindings |
finally | epilogue | forms run once after the last iteration, also when while, until, repeat, always, never or thereis ends the loop (but not after a return) |
do, doing | unconditional | evaluates the following compound forms (lists) every iteration |
return | unconditional | returns the value of the form (or it) immediately, without finally |
collect, collecting | accumulation | collects values into a list, in order |
append, appending | accumulation | appends lists, copying them |
nconc, nconcing | accumulation | concatenates lists destructively |
count, counting | accumulation | counts how often the form is true |
sum, summing | accumulation | adds the values of the form |
maximize, maximizing, minimize, minimizing | accumulation | keep the largest or smallest value; nil if there was none |
into | accumulation | puts the accumulation in a named variable, which is then usable and is not the value returned by default |
if, when, unless | conditional | perform a clause (or several joined by and) if the test is true or false; when is the same as if |
else, end | conditional | else gives the other branch; end closes the conditional, and is needed when a clause that follows would otherwise join it |
it | conditional | in a conditional, the value of the test, for use in collect it, return it |
and | joiner | joins with variables, for subclauses (in parallel) and clauses inside a conditional |
while, until | termination | ends the loop when the test is false (while) or true (until), running finally |
repeat | termination | iterates a fixed number of times |
always, never | termination | return nil at once if the form is false (always) or true (never); otherwise the loop returns t |
thereis | termination | returns the form’s value if it is not nil |
of-type | type | precedes a full type specifier for a variable: for x of-type (integer 0 9) in l; simple types (fixnum, float, t, nil) do not need it |
The for and as subclauses
| Form | Words | Example |
|---|---|---|
| Arithmetic | from, upfrom, downfrom; to, upto, below, downto, above; by | for i from 0 below n by 2, for i downfrom 10 to 1, for i to 5 (from 0) |
| In a list | in, by | for x in list, for x in list by #'cddr |
| On a list | on, by | for tail on list (successive tails) |
| Equals-then | =, then | for x = (f) then (g x); with no then the form is evaluated every iteration |
| Across a vector | across | for c across "abc" |
| Hash table | being, each, the, hash-key, hash-keys, hash-value, hash-values, in, of, using | for k being the hash-keys of h using (hash-value v) |
| Package | being, symbol, symbols, present-symbol, present-symbols, external-symbol, external-symbols, in, of | for s being the external-symbols of :cl |
Variables can be destructured: for (a b) in pairs, for (k . v) in alist, with (x y) = (values 1 2). The for … and for … form steps in parallel; separate for clauses step in sequence.
(loop for x in '(1 2 3 4 5 6)
for i from 0
when (evenp x) collect (list i x) into evens
else sum x into odd-total
finally (return (values evens odd-total)))
(loop named outer
for row in rows
do (loop for cell in row
when (null cell) do (return-from outer :hole))
finally (return :full))
(loop with table = (make-hash-table)
for word in words
do (incf (gethash word table 0))
finally (return (loop for k being the hash-keys of table using (hash-value n)
collect (cons k n))))
Behaviours worth knowing
- With no accumulation clause and no
return, the loop returnsnil. With several accumulations that have nointo, they must be compatible (acollectand anappendtogether are, acollectand asumare not). - Whether an iteration variable may be used in the
finallyclause, and what it holds there, depends on the kind offorclause, so portable code leaves it alone. loop-finishinside a clause form ends the loop as if a termination test had triggered, so the epilogue runs.(return)does not run it.- The viewer colours
loopas a keyword, then colours the words above as directives, wherever they stand directly inside an extendedloop. Words inside nested lists are code and are coloured as code. A variable calledcountorsuminfor count from 1stays plain.