Книга: Practical Common Lisp
Termination Tests
Termination Tests
While the for
and repeat
clauses provide the basic infrastructure for controlling the number of iterations, sometimes you'll need to break out of a loop early. You've already seen how a return
clause or a RETURN
or RETURN-FROM
within a do
clause can immediately terminate the loop; but just as there are common patterns for accumulating values, there are also common patterns for deciding when it's time to bail on a loop. These patterns are supported in LOOP
by the termination clauses, while
, until
, always
, never
, and thereis
. They all follow the same pattern.
loop-keyword test-form
All five evaluate test-form each time through the iteration and decide, based on the resulting value, whether to terminate the loop. They differ in what happens after they terminate the loop—if they do—and how they decide.
The loop keywords while
and until
introduce the "mild" termination clauses. When they decide to terminate the loop, control passes to the epilogue, skipping the rest of the loop body. The epilogue can then return a value or do whatever it wants to finish the loop. A while
clause terminates the loop the first time the test form is false; until
, conversely, stops it the first time the test form is true.
Another form of mild termination is provided by the LOOP-FINISH
macro. This is a regular Lisp form, not a loop clause, so it can be used anywhere within the Lisp forms of a do
clause. It also causes an immediate jump to the loop epilogue. It can be useful when the decision to break out of the loop can't be easily condensed into a single form that can be used with a while
or until
clause.
The other three clauses—always
, never
, and thereis
—terminate the loop with extreme prejudice; they immediately return from the loop, skipping not only any subsequent loop clauses but also the epilogue. They also provide a default value for the loop even when they don't cause the loop to terminate. However, if the loop is not terminated by one of these termination tests, the epilogue is run and can return a value other than the default provided by the termination clauses.
Because these clauses provide their own return values, they can't be combined with accumulation clauses unless the accumulation clause has an into
subclause. The compiler (or interpreter) should signal an error at compile time if they are.The always
and never
clauses return only boolean values, so they're most useful when you need to use a loop expression as part of a predicate. You can use always
to check that the test form is true on every iteration of the loop. Conversely, never
tests that the test form evaluates to NIL
on every iteration. If the test form fails (returning NIL
in an always
clause or non-NIL
in a never
clause), the loop is immediately terminated, returning NIL
. If the loop runs to completion, the default value of T
is provided.
For instance, if you want to test that all the numbers in a list, numbers
, are even, you can write this:
(if (loop for n in numbers always (evenp n))
(print "All numbers even."))
Equivalently you could write the following:
(if (loop for n in numbers never (oddp n))
(print "All numbers even."))
A thereis
clause is used to test whether the test form is ever true. As soon as the test form returns a non-NIL
value, the loop is terminated, returning that value. If the loop runs to completion, the thereis
clause provides a default return value of NIL
.
(loop for char across "abc123" thereis (digit-char-p char)) ==> 1
(loop for char across "abcdef" thereis (digit-char-p char)) ==> NIL