• Aucun résultat trouvé

General Iteration

Dans le document 2. Data Types (Page 197-200)

Control Structure

7.7. Blocks and Exits

7.8.2. General Iteration

In contrast to loop, doand do*provide a powerful and general mechanism for repetitively recalculating many variables.

Macro]

do (f(var init step]])g)

(end-test fresultg)

fdeclarationg ftag j statementg

Macro]

do* (f(var init step]])g)

(end-test fresultg)

fdeclarationg ftag j statementg

Thedospecial form provides a generalized iteration facility, with an arbitrary number of \index variables." These variables are bound within the iteration and stepped in parallel in specied ways. They may be used both to generate successive values of interest (such as successive integers) or to accumulate re-sults. When an end condition is met, the iteration terminates with a specied value.

In general, adoloop looks like this:

(do ((var1 init1 step1)

(var2 init2 step2)

...

(varn initn stepn))

(end-test . result)

fdeclarationg

. tagbody)

A do* loop looks exactly the same except that the name do is replaced by

do*.

The rst item in the form is a list of zero or more index-variable speciers.

Each index-variable specier is a list of the name of a variablevar, an initial valueinit, and a stepping formstep. Ifinit is omitted, it defaults tonil. If stepis omitted, thevaris not changed by thedoconstruct between repetitions (though code within thedois free to alter the value of the variable by using

setq).

An index-variable specier can also be just the name of a variable. In this case, the variable has an initial value ofniland is not changed between rep-etitions. As a matter of style, it is recommended that an unadorned variable name be written only when that variable will be stored into (such as bysetq) before its rst use. If it is important that the initial value benilrather than some undened value, then it is clearer to write out(varj nil)if the initial value is intended to mean \false," or(varj '())if the initial value is intended to be an empty list.

X3J13 voted in January 1989h182i to regularize the binding formats for

do, do*, let, let*, prog,prog*, and compiler-let. In the case of doand

do* the rst edition was inconsistent the formal syntax fails to reect the fact that a simple variable name may appear, as described in the preceding paragraph. The denitions should read

Macro]

do (fvarj (var init step]])g)

(end-test fresultg)

fdeclarationg ftag j statementg

Macro]

do* (fvarj (var init step]])g)

(end-test fresultg)

fdeclarationg ftag j statementg

for consistency with the reading of the rst edition and the X3J13 vote.

Before the rst iteration, all the initforms are evaluated, and eachvar is bound to the value of its respectiveinit. This is a binding, not an assignment when the loop terminates, the old values of those variables will be restored.

Fordo, allof the initforms are evaluatedbeforeany varis bound hence all the initforms may refer to the old bindings of all the variables (that is, to the values visible before beginning execution of the doconstruct). Fordo*, the rstinitform is evaluated, then the rstvaris bound to that value, then the second initform is evaluated, then the second var is bound, and so on in general, the initj form can refer to the new binding vark if k < j, and otherwise to the oldbinding ofvark.

The second element of the loop is a list of an end-testing predicate form end-test and zero or more resultforms. This resembles a cond clause. At the beginning of each iteration, after processing the variables, theend-testis evaluated. If the result isnil, execution proceeds with the body of thedo(or

do*) form. If the result is notnil, theresultforms are evaluated in order as an implicitprogn, and thendoreturns. doreturns the results of evaluating the lastresultform. If there are noresultforms, the value ofdoisnil. Note that this is not quite analogous to the treatment of clauses in a condform,

because acondclause with noresultforms returns the (non-nil) result of the test.

At the beginning of each iteration other than the rst, the index variables are updated as follows. All the step forms are evaluated, from left to right, and the resulting values are assigned to the respective index variables. Any variable that has no associated stepform is not assigned to. Fordo, all the step forms are evaluated before any variable is updated the assignment of values to variables is done in parallel, as if bypsetq. Because allof the step forms are evaluated before anyof the variables are altered, astepform when evaluated always has access to the oldvalues ofall the index variables, even if other stepforms precede it. Fordo*, the rst stepform is evaluated, then the value is assigned to the rstvar, then the secondstepform is evaluated, then the value is assigned to the second var, and so on the assignment of values to variables is done sequentially, as if bysetq. For either door do*, after the variables have been updated, theend-test is evaluated as described above, and the iteration continues.

If theend-testof adoform isnil, the test will never succeed. Therefore this provides an idiom for \do forever": thebodyof thedois executed repeatedly, stepping variables as usual. (Theloopconstruct performs a \do forever" that steps no variables.) The innite loop can be terminated by the use ofreturn,

return-from,goto an outer level, or throw. For example:

(do ((j 0 (+ j 1)))

(nil) Do forever

(format t "~%Input ~D:" j) (let ((item (read)))

(if (null item) (return) Process items untilnilseen

(format t "~&Output ~D: ~S" j (process item)))))

The remainder of the do form constitutes an implicittagbody. Tags may appear within the body of ado loop for use bygo statements appearing in the body (but such gostatements may not appear in the variable speciers, theend-test, or theresultforms). When the end of adobody is reached, the next iteration cycle (beginning with the evaluation ofstepforms) occurs.

An implicit block named nil surrounds the entire do form. A return statement may be used at any point to exit the loop immediately.

declareforms may appear at the beginning of ado body. They apply to code in thedobody, to the bindings of thedovariables, to theinitforms, to thestepforms, to the end-test, and to theresultforms.

Compatibility note: \Old-style" MacLisp doloops, that is, those of the form(do var init step end-test. body), are not supported in Common Lisp. Such old-style loops are considered obsolete and in any case are easily converted to a new-styledo with the insertion of three pairs of parentheses. In practice the compiler can catch nearly all instances of old-style doloops because they will not have a legal format anyway.

Here are some examples of the use ofdo:

(do ((i 0 (+ i 1)) Sets every null element ofa-vectorto zero

(n (length a-vector))) ((UU i n))

(when (null (aref a-vector i)) (setf (aref a-vector i) 0)))

The construction

(do ((x e (cdr x)) (oldx x x)) ((null x))

body)

exploits parallel assignment to index variables. On the rst iteration, the value of oldxis whatever value x had before the do was entered. On succeeding iterations,oldxcontains the value thatxhad on the previous iteration.

Very often an iterative algorithm can be most clearly expressed entirely in thestepforms of ado, and the bodyis empty. For example,

(do ((x foo (cdr x)) (y bar (cdr y))

(z '() (cons (f (car x) (car y)) z))) ((or (null x) (null y))

(nreverse z)))

does the same thing as (mapcar #--'f foo bar). Note that the step compu-tation forzexploits the fact that variables are stepped in parallel. Also, the body of the loop is empty. Finally, the use ofnreverseto put an accumulated

doloop result into the correct order is a standard idiom. Another example:

Dans le document 2. Data Types (Page 197-200)