Next: , Previous: Overview of the Loop Facility, Up: The LOOP Facility


6.1.2 Variable Initialization and Stepping Clauses

6.1.2.1 Iteration Control

Iteration control clauses allow direction of loop iteration. The loop keywords for and as designate iteration control clauses. Iteration control clauses differ with respect to the specification of termination tests and to the initialization and stepping1

of loop variables. Iteration clauses by themselves do not cause the Loop Facility to return values, but they can be used in conjunction with value-accumulation clauses to return values.

All variables are initialized in the loop prologue. A variable binding has lexical scope unless it is proclaimed special; thus, by default, the variable can be accessed only by forms that lie textually within the loop. Stepping assignments are made in the loop body before any other forms are evaluated in the body.

The variable argument in iteration control clauses can be a destructuring list. A destructuring list is a tree whose non-nil atoms are variable names. See Section 6.1.1.7 (Destructuring).

The iteration control clauses for, as, and repeat must precede any other loop clauses, except initially, with, and named, since they establish variable bindings. When iteration control clauses are used in a loop, the corresponding termination tests in the loop body are evaluated before any other loop body code is executed.

If multiple iteration clauses are used to control iteration, variable initialization and stepping1 occur sequentially by default. The and construct can be used to connect two or more iteration clauses when sequential binding and stepping1 are not necessary. The iteration behavior of clauses joined by and is analogous to the behavior of the macro do with respect to do*.

The for and as clauses iterate by using one or more local loop variables that are initialized to some value and that can be modified or stepped1 after each iteration. For these clauses, iteration terminates when a local variable reaches some supplied value or when some other loop clause terminates iteration. At each iteration, variables can be stepped1 by an increment or a decrement or can be assigned a new value by the evaluation of a form). Destructuring can be used to assign values to variables during iteration.

The for and as keywords are synonyms; they can be used interchangeably. There are seven syntactic formats for these constructs. In each syntactic format, the type of var can be supplied by the optional type-spec argument. If var is a destructuring list, the type supplied by the type-spec argument must appropriately match the elements of the list. By convention, for introduces new iterations and as introduces iterations that depend on a previous iteration specification.

6.1.2.1.1 The for-as-arithmetic subclause

In the for-as-arithmetic subclause, the for or as construct iterates from the value supplied by form1 to the value supplied by form2 in increments or decrements denoted by form3. Each expression is evaluated only once and must evaluate to a number. The variable var is bound to the value of form1 in the first iteration and is stepped1

by the value of form3 in each succeeding iteration, or by 1 if form3 is not provided. The following loop keywords serve as valid prepositions within this syntax. At least one of the prepositions must be used; and at most one from each line may be used in a single subclause.

from | downfrom | upfrom

to | downto | upto | below | above

by

The prepositional phrases in each subclause may appear in any order. For example, either “from x by y” or “by y from x” is permitted. However, because left-to-right order of evaluation is preserved, the effects will be different in the case of side effects. Consider:

(let ((x 1)) (loop for i from x by (incf x) to 10 collect i))
 (1 3 5 7 9)
(let ((x 1)) (loop for i by (incf x) from x to 10 collect i))
 (2 4 6 8 10)

The descriptions of the prepositions follow:

from

The loop keyword from specifies the value from which stepping1 begins, as supplied by form1. Stepping1 is incremental by default. If decremental stepping1 is desired, the preposition downto or above must be used with form2. For incremental stepping1, the default from value is 0.

downfrom, upfrom

The loop keyword downfrom indicates that the variable var is decreased in decrements supplied by form3; the loop keyword upfrom indicates that var is increased in increments supplied by form3.

to

The loop keyword to marks the end value for stepping1 supplied in form2. Stepping1 is incremental by default. If decremental stepping1 is desired, the preposition downfrom must be used with form1, or else the preposition downto or above should be used instead of to with form2.

downto, upto

The loop keyword downto specifies decremental stepping; the loop keyword upto specifies incremental stepping. In both cases, the amount of change on each step is specified by form3, and the loop terminates when the variable var passes the value of form2. Since there is no default for form1 in decremental stepping1, a form1 value must be supplied (using from or downfrom) when downto is supplied.

below, above

The loop keywords below and above are analogous to upto and downto respectively. These keywords stop iteration just before the value of the variable var reaches the value supplied by form2; the end value of form2 is not included. Since there is no default for form1 in decremental stepping1, a form1 value must be supplied (using from or downfrom) when above is supplied.

by

The loop keyword by marks the increment or decrement supplied by form3. The value of form3 can be any positive number. The default value is 1.

In an iteration control clause, the for or as construct causes termination when the supplied limit is reached. That is, iteration continues until the value var is stepped to the exclusive or inclusive limit supplied by form2. The range is exclusive if form3 increases or decreases var to the value of form2 without reaching that value; the loop keywords below and above provide exclusive limits. An inclusive limit allows var to attain the value of form2; to, downto, and upto provide inclusive limits.

6.1.2.1.1.1 Examples of for-as-arithmetic subclause
;; Print some numbers.
 (loop for i from 1 to 3
       do (print i))
▷ 1
▷ 2
▷ 3
 NIL

;; Print every third number.
 (loop for i from 10 downto 1 by 3
       do (print i))
▷ 10
▷ 7
▷ 4
▷ 1
 NIL

;; Step incrementally from the default starting value.
 (loop for i below 3
       do (print i))
▷ 0
▷ 1
▷ 2
 NIL
6.1.2.1.2 The for-as-in-list subclause

In the for-as-in-list subclause, the for or as construct iterates over the contents of a list. It checks for the end of the list as if by using endp. The variable var is bound to the successive elements of the list in form1 before each iteration. At the end of each iteration, the function step-fun is applied to the list; the default value for step-fun is cdr. The loop keywords in and by serve as valid prepositions in this syntax. The for or as construct causes termination when the end of the list is reached.

6.1.2.1.2.1 Examples of for-as-in-list subclause
;; Print every item in a list.
 (loop for item in '(1 2 3) do (print item))
▷ 1
▷ 2
▷ 3
 NIL

;; Print every other item in a list.
 (loop for item in '(1 2 3 4 5) by #'cddr
       do (print item))
▷ 1
▷ 3
▷ 5
 NIL

;; Destructure a list, and sum the x values using fixnum arithmetic.
 (loop for (item . x) of-type (t . fixnum) in '((A . 1) (B . 2) (C . 3))
       unless (eq item 'B) sum x)
 4
6.1.2.1.3 The for-as-on-list subclause

In the for-as-on-list subclause, the for or as construct iterates over a list. It checks for the end of the list as if by using atom. The variable var is bound to the successive tails of the list in form1. At the end of each iteration, the function step-fun is applied to the list; the default value for step-fun is cdr. The loop keywords on and by serve as valid prepositions in this syntax. The for or as construct causes termination when the end of the list is reached.

6.1.2.1.3.1 Examples of for-as-on-list subclause
;; Collect successive tails of a list.
 (loop for sublist on '(a b c d)
       collect sublist)
 ((A B C D) (B C D) (C D) (D))

;; Print a list by using destructuring with the loop keyword ON.
 (loop for (item) on '(1 2 3)
       do (print item))
▷ 1
▷ 2
▷ 3
 NIL

6.1.2.1.4 The for-as-equals-then subclause

In the for-as-equals-then subclause the for or as construct initializes the variable var by setting it to the result of evaluating form1 on the first iteration, then setting it to the result of evaluating form2 on the second and subsequent iterations. If form2 is omitted, the construct uses form1 on the second and subsequent iterations. The loop keywords = and then serve as valid prepositions in this syntax. This construct does not provide any termination tests.

6.1.2.1.4.1 Examples of for-as-equals-then subclause
;; Collect some numbers.
 (loop for item = 1 then (+ item 10)
       for iteration from 1 to 5
       collect item)
 (1 11 21 31 41)
6.1.2.1.5 The for-as-across subclause

In the for-as-across subclause the for or as construct binds the variable var to the value of each element in the array vector. The loop keyword across marks the array vector; across is used as a preposition in this syntax. Iteration stops when there are no more elements in the supplied array that can be referenced. Some implementations might recognize a the special form in the vector form to produce more efficient code.

6.1.2.1.5.1 Examples of for-as-across subclause
 (loop for char across (the simple-string (find-message channel))
       do (write-char char stream))
6.1.2.1.6 The for-as-hash subclause

In the for-as-hash subclause the for or as construct iterates over the elements, keys, and values of a hash-table. In this syntax, a compound preposition is used to designate access to a hash table. The variable var takes on the value of each hash key or hash value in the supplied hash-table. The following loop keywords serve as valid prepositions within this syntax:

being

The keyword being introduces either the Loop schema hash-key or hash-value.

each, the

The loop keyword each follows the loop keyword being when hash-key or hash-value is used. The loop keyword the is used with hash-keys and hash-values only for ease of reading. This agreement isn't required.

hash-key, hash-keys

These loop keywords access each key entry of the hash table. If the name hash-value is supplied in a using construct with one of these Loop schemas, the iteration can optionally access the keyed value. The order in which the keys are accessed is undefined; empty slots in the hash table are ignored.

hash-value, hash-values

These loop keywords access each value entry of a hash table. If the name hash-key is supplied in a using construct with one of these Loop schemas, the iteration can optionally access the key that corresponds to the value. The order in which the keys are accessed is undefined; empty slots in the hash table are ignored.

using

The loop keyword using introduces the optional key or the keyed value to be accessed. It allows access to the hash key if iteration is over the hash values, and the hash value if iteration is over the hash keys.

in, of

These loop prepositions introduce hash-table.

In effect

being {each | the} {hash-value | hash-values | hash-key | hash-keys} {in | of}

is a compound preposition.

Iteration stops when there are no more hash keys or hash values to be referenced in the supplied hash-table.

6.1.2.1.7 The for-as-package subclause

In the for-as-package subclause the for or as construct iterates over the symbols in a package. In this syntax, a compound preposition is used to designate access to a package. The variable var takes on the value of each symbol in the supplied package. The following loop keywords serve as valid prepositions within this syntax:

being

The keyword being introduces either the Loop schema symbol, present-symbol, or external-symbol.

each, the

The loop keyword each follows the loop keyword being when symbol, present-symbol, or external-symbol is used. The loop keyword the is used with symbols, present-symbols, and external-symbols only for ease of reading. This agreement isn't required.

present-symbol, present-symbols

These Loop schemas iterate over the symbols that are present in a package. The package to be iterated over is supplied in the same way that package arguments to find-package are supplied. If the package for the iteration is not supplied, the current package is used. If a package that does not exist is supplied, an error of type package-error is signaled.

symbol, symbols

These Loop schemas iterate over symbols that are accessible in a given package. The package to be iterated over is supplied in the same way that package arguments to find-package are supplied. If the package for the iteration is not supplied, the current package is used. If a package that does not exist is supplied, an error of type package-error is signaled.

external-symbol, external-symbols

These Loop schemas iterate over the external symbols of a package. The package to be iterated over is supplied in the same way that package arguments to find-package are supplied. If the package for the iteration is not supplied, the current package is used. If a package that does not exist is supplied, an error of type package-error is signaled.

in, of

These loop prepositions introduce package.

In effect

being {each | the} {symbol | symbols | present-symbol | present-symbols | external-symbol | external-symbols} {in | of}

is a compound preposition.

Iteration stops when there are no more symbols to be referenced in the supplied package.

6.1.2.1.7.1 Examples of for-as-package subclause
 (let ((*package* (make-package "TEST-PACKAGE-1")))
   ;; For effect, intern some symbols
   (read-from-string "(THIS IS A TEST)")
   (export (intern "THIS"))
   (loop for x being each present-symbol of *package*
          do (print x)))
▷ A
▷ TEST
▷ THIS
▷ IS
 NIL
6.1.2.2 Local Variable Initializations

When a loop form is executed, the local variables are bound and are initialized to some value. These local variables exist until loop iteration terminates, at which point they cease to exist. Implicit variables are also established by iteration control clauses and the into preposition of accumulation clauses.

The with construct initializes variables that are local to a loop. The variables are initialized one time only. If the optional type-spec argument is supplied for the variable var, but there is no related expression to be evaluated, var is initialized to an appropriate default value for its type. For example, for the types t, number, and float, the default values are nil, 0, and 0.0 respectively. The consequences are undefined if a type-spec argument is supplied for var if the related expression returns a value that is not of the supplied type. By default, the with construct initializes variables sequentially; that is, one variable is assigned a value before the next expression is evaluated. However, by using the loop keyword and to join several with clauses, initializations can be forced to occur in parallel; that is, all of the supplied forms are evaluated, and the results are bound to the respective variables simultaneously.

Sequential binding is used when it is desireable for the initialization of some variables to depend on the values of previously bound variables. For example, suppose the variables a, b, and c are to be bound in sequence:

 (loop with a = 1
       with b = (+ a 2)
       with c = (+ b 3)
       return (list a b c))
 (1 3 6)

The execution of the above loop is equivalent to the execution of the following code:

 (block nil
   (let* ((a 1)
          (b (+ a 2))
          (c (+ b 3)))
     (tagbody
         (next-loop (return (list a b c))
                    (go next-loop)
                    end-loop))))

If the values of previously bound variables are not needed for the initialization of other local variables, an and clause can be used to specify that the bindings are to occur in parallel:

 (loop with a = 1
       and b = 2
       and c = 3
       return (list a b c))
 (1 2 3)

The execution of the above loop is equivalent to the execution of the following code:

 (block nil
   (let ((a 1)
         (b 2)
         (c 3))
     (tagbody
         (next-loop (return (list a b c))
                    (go next-loop)
                    end-loop))))
6.1.2.2.1 Examples of WITH clause
;; These bindings occur in sequence.
 (loop with a = 1
       with b = (+ a 2)
       with c = (+ b 3)
       return (list a b c))
 (1 3 6)

;; These bindings occur in parallel.
 (setq a 5 b 10)
 10
 (loop with a = 1
       and b = (+ a 2)
       and c = (+ b 3)
       return (list a b c))
 (1 7 13)

;; This example shows a shorthand way to declare local variables
;; that are of different types.
 (loop with (a b c) of-type (float integer float)
       return (format nil "~A ~A ~A" a b c))
 "0.0 0 0.0"

;; This example shows a shorthand way to declare local variables
;; that are the same type.
 (loop with (a b c) of-type float
       return (format nil "~A ~A ~A" a b c))
 "0.0 0.0 0.0"