2.9 Advanced iteration

2.9.1 Advanced iteration methods

The following syntax is used for iterating over sequences, arrays, hash tables, and similar structures:

{for|as} var [type-spec]being {each|the} loop-method-name
[in|of]
[preposition expression]*

The var argument takes on the value of each element in the data structure expression by using the loop method specified by the loop-method-name argument. The data type of var can be specified by the type-spec argument.

For the purposes of readability, the loop keywordeach should follow the loop keywordbeing when the reference to the elements in the data structure is singular. The loop keywordthe is used afterbeing when a plural reference to the elements of the data structure is made in the loop method. These keywords are syntactically equivalent; you can useeach with plural references andthe with singular references without causing an error.

Iteration stops when there are no more elements in the specified expression that can be referenced in the way defined by the specified loop method.

For each loop method, there is a set of allowable prepositions; the most common prepositions areof andin. All of the prepositions listed under "Syntax 1" of {for|as} in Section 2.2 on page 12 are also allowed. Also, for the purposes of parsing these advanced iteration constructs, the loop keywordusing is treated as a preposition.

The loop-method-name argument can be one of the following methods; the singular and plural forms are interchangeable:

Use these loop methods to iterate over elements of Common Lisp sequences and of all structures that are accessed by the Common Lisp functionelt. Common Lisp sequences include lists, vectors, and strings. You can use the macrodefloop to define an additional form of sequential iteration; see Section 2.10 on page 49

If a Common Lisp special formthe is wrapped around the expression to be iterated over, the Compiler recognizes it as a declaration. In particular, by usingthe withelements, you can declare array types within the Loop Facility. Arrays declared to be simple, with known rank and element types, are especially optimized by Liquid Common Lisp.

;; Print the elements of a list.
> (loop for x being the elements of '(1 2 3) 
        do (print x))
1 
2 
3 
NIL

;; Print the elements of a vector up to a particular index. > (loop for x being the elements of #(a b c d e) to 3 do (print x)) A B C D NIL

;; Print the characters in a string, which is also a vector. > (loop for x being the elements of "abcde" from 2 do (print x)) #\c #\d #\e NIL

;; Print the elements of a bit vector. > (loop for x being the elements of #*101 do (print x)) 1 0 1 NIL ; Since the loop type-spec syntax is not rich enough to specify ; all of the refinements possible over sequences, you can use the ; Common Lisp THE construct around the sequence expression.

> (setq z "abc") "abc"

> (loop for x being the elements of (the simple-string z) do (print x)) #\a #\b #\c NIL

These loop methods are provided for backward compatibility to iterate over elements in arrays. They are equivalent to using the form(the array array-name) as the sequence argument for anelements method.

;; Seed an array with values, and return all of the values 
;; in the array.
> (let ((array (make-array 10)))
        (loop for i from 0 to 9
              do (setf (aref array i) i))
        (loop for a being the array-elements of array
              collect a))
(0 1 2 3 4 5 6 7 8 9)

;; Print the elements of a bit vector, which is a one-dimensional ;; array. > (loop for x being the array-elements of #*101 do (print x)) 1 0 1 NIL

;; Print the elements of a string, which is a one-dimensional ;; array. > (loop for x being the array-elements of "fun" do (print x)) #\f #\u #\n NIL

These loop methods access each key entry of a hash table. If you specify the namevalue in ausing construct with one of these loop methods, 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.

;; Create a hash table, and put some values in it.  Count the
;; number of odd keys in the table, and then collect both the keys
;; and values into an alternating list.
> (setq *ht* (make-hash-table))
#<Hash-Table 59C5AB>
> (loop for i from 1 to 10 
        do (setf (gethash i *ht*) (format nil "~D" i)))
NIL

> (loop for k being the hash-keys of *ht* when (oddp k) count k) 5

> (loop for integer being the hash-keys of *ht* using (value string) nconc (list integer string)) (1 "1" 2 "2" 3 "3" 4 "4" 5 "5" 6 "6" 7 "7" 8 "8" 9 "9" 10 "10")

These loop methods access each value entry of a hash table. If you specify the namekey in ausing construct with one of these loop methods, 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.

;;; Create a hash table, and put some values in it.  Count the
;;; number of values of length greater than 1, and then collect
;;; both the keys and values into an alternating list.
> (setq *ht* (make-hash-table))
#<Hash-Table 59C5AB>

> (loop for i from 1 to 10 do (setf (gethash i *ht*) (format nil "~D" i))) NIL

> (loop for str being the hash-values of *ht* count (> (length str) 1)) 1

> (loop for string being the hash-values of *ht* using (key integer) nconc (list integer string)) (1 "1" 2 "2" 3 "3" 4 "4" 5 "5" 6 "6" 7 "7" 8 "8" 9 "9" 10 "10")

Use these loop methods to iterate over package symbols whose string-to-symbol mappings are defined in, and are thus local to, the package. The package to be iterated over is specified in the same way that package arguments to the Common Lisp functionfind-package are specified. If you do not specify the package for the iteration, the current package is used. If you specify a package that does not exist, an error is signaled.

;; Count the symbols in a new package, which should be empty.
> (let ((pkg (make-package (gensym))))
    (loop for s being each present-symbol in pkg count s))
0

Use these loop methods to iterate over package symbols that are accessible from a given package. You specify the package to be iterated over in the same way that you specify package arguments to the Common Lisp functionfind-package. If you do not specify the package for the iteration, the current package is used. If you specify a package that does not exist, an error is signaled.

;; Gather symbols in a new package, which inherits from the 
;; LISP package.
> (let ((pkg (make-package (gensym))))
    (loop for s being each symbol in pkg collect s))
(POSITION PUSHNEW READ-FROM-STRING...)

Use these loop methods to iterate over the external symbols of a package. You specify the package to be iterated over in the same way that you specify package arguments to the Common Lisp functionfind-package. If you do not specify the package for the iteration, the current package is used. If you specify a package that does not exist, an error is signaled.

;; Gather external symbols in a new package with a few entries.
> (let ((pkg (make-package "TEMP"))) 
    (intern "A" pkg)
    (export (intern "B" pkg) pkg)
    (intern "C" pkg)
    (loop for s being the external-symbols in pkg collect s))
TEMP:B

You can define your own loop method by using the macrosdefloop ordefine-loop-method. See Section 2.10 on page 49 for more information about these macros.


The Loop Facility - 9 SEP 1996

Generated with Harlequin WebMaker