Setting Up and Tearing Down
One of the key insights the designers of the **LOOP**
language had about actual loops “in the wild” is that the loop proper is often preceded by a bit of code to set things up and then followed by some more code that does something with the values computed by the loop. A trivial example, in Perl,8 might look like this:
my $evens_sum = 0;
my $odds_sum = 0;
foreach my $i (@list_of_numbers) {
if ($i % 2) {
$odds_sum += $i;
} else {
$evens_sum += $i;
}
}
if ($evens_sum > $odds_sum) {
print "Sum of evens greater\n";
} else {
print "Sum of odds greater\n";
}
The loop proper in this code is the foreach
statement. But the foreach
loop doesn’t stand on its own: the code in the loop body refers to variables declared in the two lines before the loop.9 And the work the loop does is all for naught without the if
statement after the loop that actually reports the results. In Common Lisp, of course, the **LOOP**
construct is an expression that returns a value, so there’s even more often a need to do something after the loop proper, namely, generate the return value.
So, said the **LOOP**
designers, let’s give a way to include the code that’s really part of the loop in the loop itself. Thus, **LOOP**
provides two keywords, initially
and finally
, that introduce code to be run outside the loop’s main body.
After the initially
or finally
, these clauses consist of all the Lisp forms up to the start of the next loop clause or the end of the loop. All the initially
forms are combined into a single prologue, which runs once, immediately after all the local loop variables are initialized and before the body of the loop. The finally
forms are similarly combined into a epilogue to be run after the last iteration of the loop body. Both the prologue and epilogue code can refer to local loop variables.
The prologue is always run, even if the loop body iterates zero times. The loop can return without running the epilogue if any of the following happens:
- A
return
clause executes. **RETURN**
,**RETURN-FROM**
, or another transfer of control construct is called from within a Lisp form within the body.10- The loop is terminated by an
always
,never
, orthereis
clause, as I’ll discuss in the next section.
Within the epilogue code, **RETURN**
or **RETURN-FROM**
can be used to explicitly provide a return value for the loop. Such an explicit return value will take precedence over any value that might otherwise be provided by an accumulation or termination test clause.
To allow **RETURN-FROM**
to be used to return from a specific loop (useful when nesting **LOOP**
expressions), you can name a **LOOP**
with the loop keyword named
. If a named
clause appears in a loop, it must be the first clause. For a simple example, assume lists
is a list of lists and you want to find an item that matches some criteria in one of those nested lists. You could find it with a pair of nested loops like this:
(loop named outer for list in lists do
(loop for item in list do
(if (what-i-am-looking-for-p item)
(return-from outer item))))