Common Lisp the Language, 2nd Edition
Next: Pretty Printing
Up: Miscellaneous Features
Previous: Data Types
Destructuring allows you to bind a set of variables to a corresponding
set of values anywhere that you can normally bind a value to a single
variable. During loop
expansion, each variable in the
variable list is matched with the values in the values list. If there
are more variables in the variable list than there are values in the
values list, the remaining variables are given a value of
nil
. If there are more values than variables listed, the
extra values are discarded.
Suppose you want to assign values from a list to the variables
a
, b
, and c
. You could use one
for
clause to bind the variable numlist
to the
car of the specified expression, and then you could use another
for
clause to bind the variables a
,
b
, and c
sequentially.
;;; Collect values by using FOR constructs.
(loop for numlist in '((1 2 4.0) (5 6 8.3) (8 9 10.4))
for a integer = (first numlist)
and for b integer = (second numlist)
and for c float = (third numlist)
collect (list c b a))
=> ((4.0 2 1) (8.3 6 5) (10.4 9 8))
Destructuring makes this process easier by allowing the variables to be bound in parallel in each loop iteration. You can declare data types by using a list of type-spec arguments. If all the types are the same, you can use a shorthand destructuring syntax, as the second example following illustrates.
;;; Destructuring simplifies the process.
(loop for (a b c) (integer integer float) in
'((1 2 4.0) (5 6 8.3) (8 9 10.4))
collect (list c b a)))
=> ((4.0 2 1) (8.3 6 5) (10.4 9 8))
;;; If all the types are the same, this way is even simpler.
(loop for (a b c) float in
'((1.0 2.0 4.0) (5.0 6.0 8.3) (8.0 9.0 10.4))
collect (list c b a))
=> ((4.0 2.0 1.0) (8.3 6.0 5.0) (10.4 9.0 8.0))
If you use destructuring to declare or initialize a number of groups
of variables into types, you can use the loop keyword and
to simplify the process further.
;;; Initialize and declare variables in parallel
;;; by using the AND construct.
(loop with (a b) float = '(1.0 2.0)
and (c d) integer = '(3 4)
and (e f)
return (list a b c d e f))
=> (1.0 2.0 3 4 NIL NIL)
A data type specifier for a destructuring pattern is a tree of type specifiers with the same shape as the tree of variables, with the following exceptions:
;;; Declare X and Y to be of type VECTOR and FIXNUM, respectively.
(loop for (x y) of-type (vector fixnum) in my-list do ...)
If nil
is used in a destructuring list, no variable is
provided for its place.
(loop for (a nil b) = '(1 2 3)
do (return (list a b)))
=> (1 3)
Note that nonstandard lists can specify destructuring.
(loop for (x . y) = '(1 . 2)
do (return y))
=> 2
(loop for ((a . b) (c . d))
of-type ((float . float) (integer . integer))
in '(((1.2 . 2.4) (3 . 4)) ((3.4 . 4.6) (5 . 6)))
collect (list a b c d))
=> ((1.2 2.4 3 4) (3.4 4.6 5 6))
[It is worth noting that the destructuring facility of
loop
predates, and differs in some details from, that of
destructuring-bind
, an extension that has been provided by
many implementors of Common Lisp.-GLS]
[Loop Clause]
initially
{
expr
}*
finally
[do | doing]
{
expr
}*
finally
return
expr
The initially
construct causes the specified expression
to be evaluated in the loop prologue, which precedes all loop code
except for initial settings specified by constructs with
,
for
, or as
. The finally
construct
causes the specified expression to be evaluated in the loop epilogue
after normal iteration terminates.
The expr argument can be any non-atomic Common Lisp form.
Clauses such as return
, always
,
never
, and thereis
can bypass the
finally
clause.
The Common Lisp macro return
(or the return
loop construct) can be used after finally
to return values
from a loop. The evaluation of the return
form inside the
finally
clause takes precedence over returning the
accumulation from clauses specified by such keywords as
collect
, nconc
, append
,
sum
, count
, maximize
, and
minimize
; the accumulation values for these pre-empted
clauses are not returned by the loop if return
is used.
The constructs do
, initially
, and
finally
are the only loop keywords that take an arbitrary
number of (non-atomic) forms and group them as if by using an implicit
progn
.
Examples:
;;; This example parses a simple printed string representation
;;; from BUFFER (which is itself a string) and returns the
;;; index of the closing double-quote character.
(loop initially (unless (char= (char buffer 0) #\")
(loop-finish))
for i fixnum from 1 below (string-length buffer)
when (char= (char buffer i) #\")
return i)
;;; The FINALLY clause prints the last value of I.
;;; The collected value is returned.
(loop for i from 1 to 10
when (> i 5)
collect i
finally (print i)) `;Prints 1 line
11
=> (6 7 8 9 10)
;;; Return both the count of collected numbers
;;; as well as the numbers themselves.
(loop for i from 1 to 10
when (> i 5)
collect i into number-list
and count i into number-count
finally (return (values number-count number-list)))
=> 5 and (6 7 8 9 10)
[Loop Clause]
named
name
The named
construct allows you to assign a name to a
loop
construct so that you can use the Common Lisp special
form return-from
to exit the named loop.
Only one name may be assigned per loop; the specified name becomes the name of the implicit block for the loop.
If used, the named
construct must be the first clause in
the loop expression, coming right after the word loop
.
Example:
;;; Just name and return.
(loop named max
for i from 1 to 10
do (print i)
do (return-from max 'done)) `;Prints 1 line
1
=> DONE
Next: Pretty Printing
Up: Miscellaneous Features
Previous: Data Types
AI.Repository@cs.cmu.edu