Title: The Loop Macro
1The Loop Macro
- Many of the CL functions are actually macros
(let, progn, if, etc) - The most complicated macro in CL is probably the
Loop macro - The Loop macro gives you an alternative loop
other than do, dotimes, dolist - This Loop can act like a counter-controlled loop,
a conditional loop, or some combination - What makes the Loop unique is that it allows you
to take the result of each iteration and combine
them into a collection or into a single atom - We explore the Loop macro here, not in complete
detail because there does not seem to be any
single reference that completely understands the
Loop - instead, we will mostly explore it through
examples
2What Loop Can Do
- To start off with, we look at some specific
examples just to see how the Loop macro can be
varied to do different things
Using sum (loop for i in '(1 2 3) sum
i) 6 Combining (loop for x in '(1 2 3) for y in
'(4 5 5) collect (list x y)) ((1 4) (2 5)
(3 5)) Nesting (loop for i in '(1 2 3) collect
(list i (loop for j in '(4 5 6) collect
j))) ((1 (4 5 6)) (2 (4 5 6)) (3 (4 5 6)))
Simple counting loop (loop for i in '(1 2 3) do
(print i)) 1 2 3 NIL More complex
usage (loop for i in (1 2 3) collect (
i 2) do (print i)) 1 2 3 (2 4 6)
3Components of the Loop
- Loop prolog
- Components evaluated prior to loop execution
including variable initializations and any
initially clause - Loop body
- The forms that are executed during each
iteration, they include executable statements in
the block of code, termination tests, step
increments, and collection statements - Loop termination
- Statements in a finally clause are executed here
and whatever was accumulated is returned
(otherwise the loop returns nil)
4Example Loop Components
(loop for i from 1 to (compute-top-value)
first clause while (not (unacceptable
i)) second clause collect (square i)
third clause do (format t "Working
on D now" i) fourth clause when
(evenp i) fifth clause do
(format t "D is a non-odd number" i)
finally (format t "About to exit!")) sixth
clause
- This loop contains
- An initialization clause
- i from 1 to what (compute-top-value) returns
- Two terminating clauses (one explicit)
- either once i exceeds the top value, or if i is
unacceptable - A collection clause
- which will return the list of each (square i)
- A body
- the do statement which outputs one to two
statements depending on if the when clause is t
or nil - A finalize clause which exits once the loop is
ready to terminate
5Loop Initializations
- Loops will always start with (loop
- Followed by any initializations which are done
using for and as - for is used to initialize variables sequentially
and as is used to initialize variables in
parallel - Loop stepping and termination are determined by
supplying several possible terms - in list iterate for each item in the supplied
list - from x to y iterate across the range supplied
- alternatives are downfrom , upfrom, downto, upto,
below, above - step-size defaults to 1/-1 but you can supply
by to change the step size (such as from 3 to
10 by 2) - from and by can be given in either order (for i
by 3 from 1 to 100) - on list same as in list except that the whole
list is used during each iteration and then the
list is set to the cdr of the list - x y then z initializes x to y and then
changes it to z afterward - this makes more sense when z is an expression
like ( x 5) - across array binds the variable to each element
of a given array - (for i across x do) note that this covers the
entire array even nil items
6Examples
Note all of these examples will just print out
the sequence (loop for a in '((1 2 3) (4 5 6) (7
8 9)) do (print a)) (1 2 3) (4 5 6) (7 8
9) (loop for a from 1 to 10 by 2 do (print a))
prints 1 3 5 7 9 (loop for a downfrom 10 to 1 do
(print a)) prints 10 9 8 1 (loop for a from 1
below 10 by 4 do (print a)) prints 1 5 9 (loop
for i upfrom 10 to 20 do (print i)) prints 10
11 12 20 (loop for i on '(1 2 3 4) do (print
i)) (1 2 3 4) (2 3 4) (3 4) (4) (loop for a
1 then ( a 10) for i from 1 to 5 do (print a))
prints 1 11 21 31 41 (loop for a across (1 2
3) do (print a)) prints 1 2 3
7More on Initializations
- Any variables initialized in the loop are bound
inside the loop only and lose their scope
afterward - The with statement allows you to declare and
initialize additional local variables aside from
any declared through the initialization processes
we saw on the last slide - the with statements perform initializations
sequentially (as in let) - use and in between the initializations to perform
them in parallel - Note that it is difficult to use with/and when
using the various other initializations covered
on the last slide, so these are less useful - Example (loop with a 1 with b a collect b)
- This is an infinite loop since we did not provide
a terminating condition - Example (loop for i in (1 2 3) and a i
collect a) - returns (nil 1 2)
- while (loop for i in (1 2 3) with a i collect
a) - returns (nil nil nil)
8Accumulation Clauses
- After initialization, your loop can contain
executable statements as you would include in do,
dotimes and dolist statements - But you can also specify accumulation clauses
- collect collect the response of the next
statement into a list, which is returned after
the loop terminates - append same as collect except that the items
collected are expected to already be in a list - nconc same as append but destructive
- sum performs a running total on the value
returned by the next statement, and returns this
total when the loop terminates - count counts and returns the number of times
the next statement is true - maximize, minimize returns the max/min value
provided by the next statement across all loop
iterations
9Examples
(loop for i from 2 to 100 append (if (prime i)
(list i) nil)) note the use of append here,
if we used collect, we would get nils in our
return list (2 3 nil 5 nil 7 ) (loop for i from
2 to 100 count (prime i)) returns 25 ( of
primes in 2..100) (loop for name in '(fred sue
alice joe june) for kids in '((bob ken)
( ) ( ) (kris sunshine) ( )) collect name
append kids) (FRED BOB KEN SUE ALICE JOE KRIS
SUNSHINE JUNE) (loop for i in '(bird 3 4 turtle
(1 . 4) horse cat) when (symbolp i) collect i)
(BIRD TURTLE HORSE CAT) (loop for i across a
maximize i) Returns the maximum from array a
10Finally
- The finally clause is optional
- If specified, it is executed one time after the
final loop iteration - The finally clause can used to clean up what the
loop was doing (for instance, closing a file) - Note that the finally clause is circumvented on
certain circumstances that we will cover
(loop for i from 1 to 10 by 3 collect i finally
(print i)) 13 (1 4 7 10) (loop for i from 2 to
100 with x 0 append (if (prime i) (list i) nil)
do (if (prime i) (setf x ( x 1))) finally
(print x)) 25 (2 3 5 7 11 13 17 19 23 29 31 37
41 43 47 53 59 61 67 71 73 79 83 89 97) (loop
for i from 1 to 10 with x 2 sum (if ( (mod i
x) 0) (progn (setf x ( x 1)) i) 0) finally
(print x)) 11 54
11Repeat Clause
- Instead of using variable initialization over
some range (such as with in, from, to, etc) you
can use the repeat clause - This evaluates the next form and iterates that
many times - (loop repeat 10 ) repeats 10 times
- (loop repeat (foo x) ) repeats (foo x) times
- The form is only evaluated once
- The repeat clause does not provide you a loop
index to reference but you can make your own - (loop repeat 10 for a 1 then ( a 1) do (print
a)) - Notice here that a is a local variable but not
controlling the loop - If the form returns 0 or a negative number, the
loop body is never executed but the finally
clause is executed - Consider the following loop it will print
repeating from 0 to 9 times and then print done
(loop repeat (random 10) do (print 'repeating)
finally (print 'done)
12Prematurely Exiting A Loop
- A for loop can be prematurely terminated by using
one of - always, unless, thereis which can return a
specified value - until a given condition becomes true
- while a given condition is not false
- The first group (always, never, thereis) will
return a t/nil value from the loop upon
termination - these return t if the condition is not violated
or nil otherwise - always returns t if the condition provided is
always t - never returns t if the condition provided is
never t - thereis returns t if the condition provided is t
once (the loop terminates as soon as one instance
is found) - Note these cannot be used in conjunction with a
collection mechanism without using an into clause
see the example that follows - If there is a finally clause
- it is executed after a normal loop terminates
whether normally or by until or while - but it does not get executed if the loop is
terminated by always, never or thereis
13More Examples
(loop repeat 10 with x 1 until (gt x 10) do
(print x) (setf x ( x x))) -- prints 1 2 4 8
and returns NIL (loop for i from 120 to 130
never (prime i) do (print i)) -- prints the
list 120 through 126 and returns nil (loop for i
from 100 to 200 do (print i) thereis (prime i))
-- outputs 100 and 101 and then returns
t (loop for i from 1 to 10 until (gt ( i i) 60)
collect ( i i) into x finally (return x)) --
returns (1 4 9 16 25 36 49) notice into x which
allows us to collect a value while using a
terminating condition (loop for i across a never
(null i) sum i into x finally (return (/ x
(length a)))) Here, we are summing up all
elements of array a into variable x as long as
none are nil, the loop terminates if we find a
nil and returns nil, otherwise this returns the
average
14Some More Loop Examples
(setf calories 0) (defun eat ( ) (declare
(special calories)) (setf calories (
calories ( 50 (random
250))))) (defun hungry-p ( ) (declare
(special calories)) (lt calories
600)) (loop while (hungry-p) do
(eat)) NIL calories 640
(loop for i from 1 to 3 do (print i)
(print ( i i))) this loop has two
executable statements so prints 6 things (1 1 2 4
3 9)
(let ((stack '(a b c d e f))) (loop for item
(length stack) then (pop stack)
collect item while stack)) (6 A B C D
E F)
15Using When and Unless
- Two additional clauses that can be supplied in
the Loop macro are when and unless - Recall the functions (when condition body) and
(unless condition body) - In this case, like with other Loop clauses,
when/unless will not appear in ( ) - We can also use this with a return clause to
prematurely exit the loop (and optionally return
a value)
(loop for i from 100 to 200 when (prime i) sum
i) (loop for i in (1 2 a 3 b 4 hi) when
(numberp i) collect i) (loop for i in '(1 2 a 3 b
4) when (numberp i) unless (evenp i) do (print
i))
(loop for i in '(1 2 a 4 5) unless (numberp i)
return i sum i)
16Some Variations
- Initialization and stepping can also be done to
the values of a hash table or values in a package - We will omit those so that its not more confusing
than necessary! - If we do not supply any Loop keywords, we have an
infinite loop - (loop body) is an infinite loop on body
- we can use return statements to exit the loop to
prevent it being infinite - Loops can be nested
- Loop iterators can be applied in parallel
- (loop for x from 1 to 10 for y from 11 to 20 do
(print (list x y)) prints the list ((1 11) (2
12) (3 13)(10 20)) - Loop clauses can be grouped
- see the example on the next slide
- The variable it is a specially reserved variable
- (loop for i in '(1 2 3 4 5 6) when (and (gt i 3)
i) collect it) - returns (4 5 6)
- (loop for i in '(1 2 3 4 5 6) when (and (gt i 3)
i) return it) - returns 4
17Grouping Clauses Example
(loop for i in '(1 324 2345 323 2 4 235 252)
when (oddp i) do (print i) and collect i
into odd-numbers and do (terpri)
else i is even.
collect i into even-numbers finally (return
(values odd-numbers even-numbers))) gt 1 gt gt
2345 gt gt 323 gt gt 235 and returns (1 2345 323
235), (324 2 4 252)
18Follow These Rules
- The Loop mechanism can be treacherous to use and
difficult to understand how and when to use it - However, if you want to try, your loop must be in
the proper order - Start with your initially clause (if any)
- Follow it by your for, with and repeat clause(s)
- Next you can supply your conditional clauses
(when/unless) and unconditional clauses (do) and
any accumulation statements or termination
test(s) - End with your finally clause
- When the loop is iterating, the body is executed
by - first stepping through any iteration control
variables - then doing in the order that they appear in the
loop body - any conditional or unconditional execution
statements, any accumulation clauses, any
termination test clauses - if any of the clauses in the loop body terminate
the loop, the rest of the body is skipped and the
loop returns - the finally clause is executed unless the loop is
terminated by a return, always, never or thereis
clause