mirror of
https://github.com/PDP-10/its.git
synced 2026-04-30 13:42:06 +00:00
Added documentation for lisp and lisp libraries.
This commit is contained in:
committed by
Lars Brinkhoff
parent
e9619de352
commit
88aae69a9e
973
doc/_info_/lisp.loop
Executable file
973
doc/_info_/lisp.loop
Executable file
@@ -0,0 +1,973 @@
|
||||
.c Thursday July 10,1980 3:59 LQ+5D.3H.56M.26S. -*- Text -*-
|
||||
|
||||
.chapter "Introduction"
|
||||
LOOP is a Lisp macro which provides a programmable iteration
|
||||
facility. The same LOOP module operates compatibly in both Lisp
|
||||
Machine Lisp and Maclisp (PDP-10 and Multics). LOOP was inspired by
|
||||
the "FOR" facility of CLISP in InterLisp; however, it is not
|
||||
compatible and differs in several details.
|
||||
|
||||
The general approach is that a form introduced by the word
|
||||
3loop* generates a single program loop, into which a large variety
|
||||
of features can be incorporated. The loop consists of some
|
||||
initialization (2prologue*) code, a body which may be executed
|
||||
several times, and some exit (2epilogue*) code. Variables may be
|
||||
declared local to the loop. The
|
||||
features are concerned with loop variables, deciding when to end the
|
||||
iteration, putting user-written code into the loop, returning a value
|
||||
from the construct, and iterating a variable through various real or
|
||||
virtual sets of values.
|
||||
The 3loop* form consists of a series of clauses, each
|
||||
introduced by a keyword symbol. Forms appearing in or implied by the
|
||||
clauses of a 3loop* form are classed as those to be executed as
|
||||
initialization code, body code, and/or exit code, but aside from that
|
||||
they are executed strictly in the order implied by the original
|
||||
composition. Thus, just as in ordinary Lisp code, side-effects may
|
||||
be used, and one piece of code may depend on following another for
|
||||
its proper operation. This is the principal philosophy difference
|
||||
from InterLisp's "FOR" facility.
|
||||
Note that 3loop* forms are intended to look like stylized English
|
||||
rather than Lisp code. There is a notably low density of parentheses,
|
||||
and many of the keywords are accepted in several synonymous forms to
|
||||
allow writing of more euphonious and grammatical English. Some
|
||||
find this notation verbose and distasteful, while others find it
|
||||
flexible and convenient. The former are invited to stick to 3do*.
|
||||
.space 1
|
||||
.group
|
||||
.lisp
|
||||
(defun print-elements-of-list (list-of-elements)
|
||||
(loop for element in list-of-elements
|
||||
do (print element)))
|
||||
.end_lisp
|
||||
The above function prints each element in its argument, which
|
||||
should be a list. It returns 3nil*.
|
||||
.end_group
|
||||
.space 1
|
||||
.group
|
||||
.lisp
|
||||
(defun extract-interesting-numbers (start-value end-value)
|
||||
(loop for number from start-value to end-value
|
||||
when (interesting-p number) collect number))
|
||||
.end_lisp
|
||||
The above function takes two arguments, which should be
|
||||
fixnums, and returns a list of all the numbers in that range
|
||||
(inclusive) which satisfy the predicate 3interesting-p*.
|
||||
.end_group
|
||||
.space 1
|
||||
.group
|
||||
.lisp
|
||||
(defun find-maximum-element (array)
|
||||
(loop for i from 0 below (cadr (arraydims array))
|
||||
maximize (funcall array i)))
|
||||
.end_lisp
|
||||
3Find-maximum-element* returns the maximum of the elements
|
||||
of its argument, a one-dimensional array.
|
||||
.end_group
|
||||
.group
|
||||
.space 1
|
||||
.lisp
|
||||
(defun remove (object list)
|
||||
(loop for element in list
|
||||
unless (equal object element) collect element))
|
||||
.end_lisp
|
||||
3Remove* is like the Lisp function 3delete*, except
|
||||
that it copies the list rather than destructively splicing out
|
||||
elements.
|
||||
.end_group
|
||||
.space 1
|
||||
.group
|
||||
.lisp
|
||||
(defun find-frob (list)
|
||||
(loop for element in list
|
||||
when (frobp element) return element
|
||||
finally (error '|Frob not found in list| list)))
|
||||
.end_lisp
|
||||
This returns the first element of its list argument which
|
||||
satisfies the predicate 3frobp*. If none is found, an error is
|
||||
generated.
|
||||
.end_group
|
||||
|
||||
.chapter "Clauses"
|
||||
|
||||
Internally, LOOP constructs a 3prog* which includes
|
||||
variable bindings, pre-iteration (initialization) code,
|
||||
post-iteration (exit) code, the body of the iteration, and stepping
|
||||
of variables of iteration to their next values (which happens on
|
||||
every iteration after executing the body).
|
||||
A 2clause* consists of the keyword symbol and any other
|
||||
Lisp forms and keywords which it deals with. For example,
|
||||
.lisp
|
||||
(loop for x in l do (print x)),
|
||||
.end_lisp
|
||||
contains two clauses, "7for x in l*" and "7do (print x)*".
|
||||
Certain of the
|
||||
parts of the clause will be described as being 2expressions*, e.g.
|
||||
"(print x)" in the above.
|
||||
An expression can be a single Lisp form, or a series of forms
|
||||
implicitly collected with 3progn*. An expression is terminated by
|
||||
the next following atom, which is taken to be a keyword. Thus, syntax
|
||||
allows only the first form in an expression to be atomic, but makes
|
||||
misspelled keywords more easily detectable.
|
||||
|
||||
Bindings and iteration variable steppings may be performed
|
||||
either sequentially or in
|
||||
'cindex sequential vs parallel binding and initialization
|
||||
parallel, which affects how the stepping of one iteration
|
||||
variable may depend on the value of another. The syntax for
|
||||
distinguishing the two will be described with the corresponding clauses.
|
||||
When a set of things is "in parallel", all of the bindings produced
|
||||
will be performed in parallel by a single lambda binding.
|
||||
Subsequent bindings will be performed inside of that binding environment.
|
||||
|
||||
.section "Iteration-Producing Clauses"
|
||||
|
||||
These clauses all create a 2variable of iteration*, which
|
||||
is bound locally to the loop and takes on a new value on each
|
||||
successive iteration. Note that if more than one iteration-producing
|
||||
clause is used in the same loop, several variables are created which
|
||||
all step together through their values; when any of the iterations
|
||||
terminates, the entire loop terminates. Nested iterations are not
|
||||
generated; for those, you need a second 3loop* form in the body of
|
||||
the loop.
|
||||
|
||||
All of the iteration-producing clauses initially defined are
|
||||
introduced with the keyword 3for* (or 3as*, which is
|
||||
synonomous).
|
||||
'cindex parallel vs. sequential iteration stepping
|
||||
3For* clauses may be clustered into groups, the variables of
|
||||
iteration of which are to be stepped in
|
||||
parallel, by introducing the additional clauses with 3and* instead
|
||||
of 3for* or 3as*. For example, the following iterates over the
|
||||
elements in a list, and also has a variable for the element from the
|
||||
previous iteration:
|
||||
.lisp
|
||||
(loop for item in list and previous-item = 'foo then item
|
||||
do ...)
|
||||
.end_lisp
|
||||
During the first iteration, 3previous-item* has the value
|
||||
3foo*; in subsequent iterations, it has the value of 3item*
|
||||
from the previous iteration. Note that this would not work if the
|
||||
stepping were not performed in parallel.
|
||||
|
||||
The order of evaluation in iteration-producing clauses is that
|
||||
'cindex order of evaluation in iteration clauses
|
||||
those expressions which are only evaluated once are evaluated in order
|
||||
at the beginning of the form, during the variable-binding phase, while
|
||||
those expressions which are evaluated each time around the loop are
|
||||
evaluated in order in the body.
|
||||
|
||||
These are the iteration-producing clauses. Optional parts are
|
||||
enclosed in curly brackets.
|
||||
|
||||
.table 3 250 500
|
||||
|
||||
.item for 2var* {2data-type*} in 2expr1* {by 2expr2*}
|
||||
.kindex for
|
||||
This iterates over each of the elements in the list 2expr1*. If
|
||||
the 3by* subclause is present, 2expr2* is evaluated once
|
||||
on entry to the loop
|
||||
to supply the function to be used to fetch successive sublists,
|
||||
instead of 3cdr*.
|
||||
|
||||
.item for 2var* on 2expr1* {by 2expr2*}
|
||||
.kindex for
|
||||
This is like the previous 3for* format, except that 2var* is
|
||||
set to successive tails of the list instead of successive elements.
|
||||
|
||||
.item for 2var* {2data-type*} = 2expr*
|
||||
.kindex for
|
||||
On each iteration, 2expr* is evaluated and 2var* is set to the result.
|
||||
|
||||
.item for 2var* {2data-type*} = 2expr1* then 2expr2*
|
||||
.kindex for
|
||||
2Var* is bound to 2expr1* when the loop is entered, and set to
|
||||
2expr2* on all succeeding iterations.
|
||||
|
||||
.item for 2var* {2data-type*} from 2expr1* {to 2expr2*} {by 2expr3*}
|
||||
.kindex for
|
||||
'c i can't read moon's handwriting!
|
||||
This performs numeric iteration. 2Var* is initialized to
|
||||
2expr1*, and on each succeeding iteration is incremented by
|
||||
2expr3* (default 31*). If the 3to* phrase is given, the
|
||||
iteration terminates when 2var* becomes greater than 2expr2*.
|
||||
Each of the
|
||||
expressions is evaluated only once, and the 3to* and 3by*
|
||||
phrases may be written in either order. 3Downto* may be used instead
|
||||
of 3to*, in which case 2var* is decremented by the step value,
|
||||
and the endtest is adjusted accordingly. If 3below* is used
|
||||
instead of 3to*, or 3above* instead of 3downto*, the
|
||||
iteration will be terminated before 2expr2* is reached, rather
|
||||
than after. Note that the 3to* variant appropriate for the
|
||||
direction of stepping must be used for the endtest to be formed
|
||||
correctly, i.e. the code will not work if 2expr3* is negative or
|
||||
zero. If no limit specifying clause is given, then the direction of
|
||||
the stepping may be specified as being decreasing by using
|
||||
3downfrom* instead of 3from*. 3Upfrom* may also be used
|
||||
instead of 3from*; it forces the stepping direction to be
|
||||
increasing.
|
||||
The 2data-type* defaults to 3fixnum*.
|
||||
|
||||
.item for 2var* {2data-type*} being 2expr* and its 2path* ...
|
||||
.item1 for 2var* {2data-type*} being {each} 2path* ...
|
||||
.kindex for
|
||||
This provides a user-definable iteration facility. 2Path* names
|
||||
the manner in which the iteration is to be performed. The ellipsis
|
||||
indicates where various path dependent preposition/expression pairs
|
||||
may appear. See the section on Iteration Paths
|
||||
((iteration-path-page)) for complete documentation.
|
||||
.end_table
|
||||
|
||||
.section "Bindings"
|
||||
.setq with-clause page
|
||||
.kindex with
|
||||
'cindex variable bindings
|
||||
The 3with* keyword may be used to establish initial
|
||||
bindings, that is, variables which are local to the loop but are only
|
||||
set once, rather than on each iteration. The 3with* clause looks like:
|
||||
.lisp
|
||||
3with 2var1* {2data-type*} {= 2expr1*}
|
||||
{and 2var2* {2data-type*} {= 2expr2*}}...*
|
||||
.end_lisp
|
||||
If no 2expr* is given, the variable is initialized to the
|
||||
appropriate value for its data type, usually 3nil*.
|
||||
3With* bindings linked by 3and* are performed in
|
||||
parallel; those not linked are performed sequentially. That is,
|
||||
.lisp
|
||||
(loop with a = (foo) and b = (bar) ...)
|
||||
.end_lisp
|
||||
binds the variables like
|
||||
.lisp
|
||||
((lambda (a b) ...)
|
||||
(foo) (bar))
|
||||
.end_lisp
|
||||
whereas
|
||||
.lisp
|
||||
(loop with a = (foo) with b = (barprime a) ...)
|
||||
.end_lisp
|
||||
binds the variables like
|
||||
.lisp
|
||||
((lambda (a)
|
||||
((lambda (b) ...)
|
||||
(barprime a)))
|
||||
(foo))
|
||||
.end_lisp
|
||||
All 2expr*'s in 3with* clauses are evaluated in the order they
|
||||
are written, upon entrance to the loop rather than where they appear
|
||||
in the body. Thus good style suggests that 3with* clauses be
|
||||
placed first in the loop.
|
||||
|
||||
For binding more than one variable with no particular
|
||||
initialization, one may use the construct
|
||||
.lisp
|
||||
3with 2variable-list* {2data-type-list*} {and ...}*
|
||||
.end_lisp
|
||||
as in
|
||||
.lisp
|
||||
with (i j k t1 t2) (fixnum fixnum fixnum) ...
|
||||
.end_lisp
|
||||
which is a useful special case of 2destructuring*
|
||||
((destructuring-section)).
|
||||
|
||||
|
||||
.section "Entrance and Exit"
|
||||
|
||||
.table 3 250 500
|
||||
.item initially 2expression*
|
||||
.kindex initially
|
||||
This puts 2expression* into the 2prologue* of the iteration. It
|
||||
will be evaluated before any other initialization code other than the
|
||||
initial bindings. For the sake of good style, the 3initially*
|
||||
clause should therefore be placed after any 3with* clauses but
|
||||
before the main body of the loop.
|
||||
|
||||
.item finally 2expression*
|
||||
.kindex finally
|
||||
This puts 2expression* into the 2epilogue* of the loop, which is
|
||||
evaluated when the iteration terminates (other than by an explicit
|
||||
3return*). For stylistic reasons, then, this clause should appear
|
||||
last in the loop body. Note that certain clauses may generate code
|
||||
which terminates the iteration without running the epilogue code;
|
||||
this behaviour is noted with those clauses.
|
||||
|
||||
.end_table
|
||||
|
||||
.section "Side Effects"
|
||||
.setq side-effects-section css-number
|
||||
.table 3 250 500
|
||||
.item do 2expression*
|
||||
.item1 doing 2expression*
|
||||
.kindex do doing
|
||||
2Expression* is evaluated each time through the loop.
|
||||
.end_table
|
||||
|
||||
.section "Values"
|
||||
.setq values-section css-number
|
||||
The following clauses accumulate a return value for the
|
||||
iteration in some manner. The general form is
|
||||
.lisp
|
||||
32type-of-collection expr* {2data-type*} {into 2var*}*
|
||||
.end_lisp
|
||||
where 2type-of-collection* is a 3loop* keyword, and 2expr*
|
||||
is the thing being "accumulated" somehow. If no 3into* is
|
||||
specified, then the accumulation will be returned when the 3loop*
|
||||
terminates. If there is an 3into*, then when the epilogue of the
|
||||
3loop* is reached, 2var* (a variable automatically bound
|
||||
locally in the loop) will have been set to the accumulated
|
||||
result and may be used by the epilogue code. In this way, a user may
|
||||
accumulate and somehow pass back multiple values from a single
|
||||
3loop*, or use them during the loop. It is safe to reference
|
||||
these variables during the loop, but they should not be modified
|
||||
until the epilogue code of the loop is reached.
|
||||
For example,
|
||||
.lisp
|
||||
(loop for x in list
|
||||
collect (foo x) into foo-list
|
||||
collect (bar x) into bar-list
|
||||
collect (baz x) into baz-list
|
||||
finally (return (list foo-list bar-list baz-list)))
|
||||
.end_lisp
|
||||
which has the same effect as
|
||||
.lisp
|
||||
(do ((g0001 l (cdr g0001)) (x) (foo-list) (bar-list) (baz-list))
|
||||
((null g0001)
|
||||
(list (nreverse foo-list)
|
||||
(nreverse bar-list)
|
||||
(nreverse baz-list)))
|
||||
(setq x (car g0001))
|
||||
(setq foo-list (cons (foo x) foo-list))
|
||||
(setq bar-list (cons (bar x) bar-list))
|
||||
(setq baz-list (cons (baz x) baz-list)))
|
||||
.end_lisp
|
||||
|
||||
.table 3 250 500
|
||||
|
||||
.item collect 2expr* {into 2var*}
|
||||
.item1 collecting ...
|
||||
.kindex collect collecting
|
||||
.setq collect-clause page
|
||||
This causes the values of 2expr* on each iteration to be collected
|
||||
into a list.
|
||||
|
||||
.item nconc 2expr* {into 2var*}
|
||||
.item1 nconcing ...
|
||||
.item1 append ...
|
||||
.item1 appending ...
|
||||
.kindex nconc nconcing append appending
|
||||
These are like 3collect*, but the results are 3nconc*ed or
|
||||
3append*ed together as appropriate.
|
||||
3collecting*:3mapcar*::3nconcing*:3mapcan*.
|
||||
|
||||
.item count 2expr* {into 2var*}
|
||||
.item1 counting ...
|
||||
.kindex count counting
|
||||
If 2expr* evaluates non-3nil*, a counter is incremented.
|
||||
The 2data-type* is always 3fixnum*.
|
||||
|
||||
.item sum 2expr* {2data-type*} {into 2var*}
|
||||
.item1 summing ...
|
||||
.kindex sum summing
|
||||
Evaluates 2expr* on each iteration, and accumulates the sum of all
|
||||
the values. 2Data-type* defaults to
|
||||
3number*, which for all practical purposes is 3notype*.
|
||||
|
||||
.item maximize 2expr* {2data-type*} {into 2var*}
|
||||
.item1 minimize ...
|
||||
.kindex maximize minimize
|
||||
Computes the maximum (or minimum) of 2expr* over all
|
||||
iterations. 2Data-type* defaults to 3number*.
|
||||
.end_table
|
||||
|
||||
Not only may there be multiple 2accumulations* in a
|
||||
'cindex multiple accumulations
|
||||
3loop*, but a single 2accumulation* may come from multiple
|
||||
places 2within the same 3loop* form*. Obviously, the types of
|
||||
the collection must be compatible. 3Collect*, 3nconc*, and
|
||||
3append* may all be mixed, as may 3sum* and 3count*, and
|
||||
3maximize* and 3minimize*. For example,
|
||||
.lisp
|
||||
(loop for x in '(a b c) for y in '((1 2) (3 4) (5 6))
|
||||
collect x
|
||||
append y)
|
||||
=> (a 1 2 b 3 4 c 5 6)
|
||||
.end_lisp
|
||||
.group
|
||||
The following computes the average of the entries in the list
|
||||
2list-of-frobs*:
|
||||
.lisp
|
||||
(loop for x in list-of-frobs
|
||||
count t into count-var
|
||||
sum x into sum-var
|
||||
finally (return (quotient sum-var count-var)))
|
||||
.end_lisp
|
||||
.end_group
|
||||
|
||||
.section "Endtests"
|
||||
.cindex terminating the iteration
|
||||
The following clauses may be used to provide additional
|
||||
control over when the iteration gets terminated, possibly causing
|
||||
exit code (due to 3finally*) to be performed and possibly returning
|
||||
a value (e.g., from 3collect*).
|
||||
|
||||
.table 3 250 500
|
||||
.item while 2expr*
|
||||
.kindex while
|
||||
If 2expr* evaluates to 3nil*, the loop is exited, performing
|
||||
exit code (if any), and returning any accumulated value. The
|
||||
test is placed in the body of the loop where it is written. It may
|
||||
appear between sequential 3for* clauses.
|
||||
|
||||
.item until 2expr*
|
||||
.kindex until
|
||||
Identical to 3while (not 2expr*)*.
|
||||
.end_table
|
||||
|
||||
This may be needed, for example, to step through a strange
|
||||
data structure, as in
|
||||
.lisp
|
||||
(loop for concept = 2expr* then (superior-concept concept)
|
||||
until (eq concept [summum-genus])
|
||||
...)
|
||||
.end_lisp
|
||||
|
||||
.section "Aggregated Boolean Tests"
|
||||
|
||||
.table 3 250 500
|
||||
.item always 2expr*
|
||||
.kindex always
|
||||
If 2expr* evaluates to 3nil*, the iteration is terminated and
|
||||
3nil* returned; otherwise, 3t* will be returned when the loop
|
||||
finishes, after the epilogue code (if any, as specified with the
|
||||
3finally* clause) has been run.
|
||||
|
||||
.item never 2expr*
|
||||
.kindex never
|
||||
This is like 3always (not 2expr*)*.
|
||||
|
||||
.item thereis 2expr*
|
||||
.kindex thereis
|
||||
If 2expr* evaluates non-3nil*, then the iteration is
|
||||
terminated and that value is returned, without running the epilogue
|
||||
code.
|
||||
.end_table
|
||||
|
||||
.section "Conditionalization"
|
||||
.cindex conditionalizing clause(s)
|
||||
These clauses may be used to "conditionalize" the following
|
||||
clause. They may precede any of the side-effecting or value-producing
|
||||
clauses, such as 3do*, 3collect*, or 3always*.
|
||||
|
||||
.table 3 250 500
|
||||
.item when 2expr*
|
||||
.item1 if 2expr*
|
||||
.kindex when if
|
||||
.space 0
|
||||
.c Make sure this starts it on a new line....
|
||||
.c .break doesn't do it.
|
||||
.c Will a ".space 0" do it?
|
||||
If 2expr* evaluates to 3nil*, the following clause will be
|
||||
skipped, otherwise not.
|
||||
|
||||
.item unless 2expr*
|
||||
.kindex unless
|
||||
This is equivalent to 3when (not 2expr*))*.
|
||||
.end_table
|
||||
|
||||
Multiple conditionalization clauses may appear in sequence.
|
||||
If one test fails, then any following tests in the immediate sequence,
|
||||
and the clause being conditionalized, are skipped.
|
||||
|
||||
Multiple clauses may be conditionalized under the same test by
|
||||
joining them with 3and*, as in
|
||||
.lisp
|
||||
(loop for i from a to b
|
||||
when (zerop (remainder i 3))
|
||||
collect i and do (print i))
|
||||
.end_lisp
|
||||
which returns a list of all multiples of 33* from 2a* to
|
||||
2b* (inclusive) and prints them as they are being collected.
|
||||
Conditionals may be nested. For example,
|
||||
.lisp
|
||||
(loop for i from a to b
|
||||
when (zerop (remainder i 3))
|
||||
do (print i)
|
||||
and when (zerop (remainder i 2))
|
||||
collect i)
|
||||
.end_lisp
|
||||
returns a list of all multiples of 36* from 2a* to 2b*,
|
||||
and prints all multiples of 33* from 2a* to 2b*.
|
||||
|
||||
Useful with the conditionalization clauses is the 3return*
|
||||
clause, which causes an explicit return of its "argument" as
|
||||
the value of the iteration, bypassing any epilogue code. That is,
|
||||
.lisp
|
||||
3when 2expr1* return 2expr2**
|
||||
.end_lisp
|
||||
is equivalent to
|
||||
.lisp
|
||||
3when 2expr1* do (return 2expr2*)*
|
||||
.end_lisp
|
||||
Conditionalization of one of the "aggregated boolean value"
|
||||
clauses simply causes the test which would cause the iteration to
|
||||
terminate early not to be performed unless the condition succeeds.
|
||||
For example,
|
||||
.lisp
|
||||
(loop for x in l
|
||||
when (significant-p x)
|
||||
do (print x) (princ "is significant.")
|
||||
and thereis (extra-special-significant-p x))
|
||||
.end_lisp
|
||||
|
||||
.group
|
||||
The format of a conditionalization and following clause is
|
||||
typically something like
|
||||
.lisp
|
||||
3when 2expr1* 2keyword* 2expr2**
|
||||
.end_lisp
|
||||
If 2expr2* is the keyword 3it*, then a variable is generated to
|
||||
hold the value of 2expr1*, and that variable gets substituted for
|
||||
2expr2*. Thus, the composition
|
||||
.lisp
|
||||
3when 2expr* return it*
|
||||
.end_lisp
|
||||
is equivalent to the clause
|
||||
.lisp
|
||||
3thereis 2expr**
|
||||
.end_lisp
|
||||
and one may collect all non-null values in an iteration by saying
|
||||
.lisp
|
||||
3when 2expression* collect it*
|
||||
.end_lisp
|
||||
If multiple clauses are joined with 3and*, the 3it* keyword
|
||||
may only be used in the first. If multiple 3when*s,
|
||||
3unless*es, and/or 3if*s occur in sequence, the value
|
||||
substituted for 3it* will be that of the last test performed.
|
||||
.end_group
|
||||
|
||||
.chapter "LOOP Synonyms"
|
||||
|
||||
.defmac define-loop-macro
|
||||
.lisp
|
||||
(define-loop-macro 2keyword*)
|
||||
.end_lisp
|
||||
may be used to make 2keyword*, a 3loop* keyword (such as
|
||||
3for*), into a LISP macro which may introduce a 3loop* form.
|
||||
For example, after evaluating
|
||||
.lisp
|
||||
(define-loop-macro for),
|
||||
.end_lisp
|
||||
one may now write an iteration as
|
||||
.lisp
|
||||
(for i from 1 below n do ...)
|
||||
.end_lisp
|
||||
.end_defmac
|
||||
|
||||
.chapter "Data Types"
|
||||
.setq data-type-section page
|
||||
.cindex data type keywords
|
||||
In many of the clause descriptions, an optional 2data-type*
|
||||
is shown. A 2data-type* in this sense is an atomic symbol, and is
|
||||
recognizable as such by LOOP. LOOP interfaces to a module which
|
||||
defines how declarations and initializations are to be performed for
|
||||
various data types. However, it recognizes several types specially so
|
||||
that that module need not be present in order for them to be used:
|
||||
.table 3 250 500
|
||||
.item fixnum
|
||||
An implementation-dependent limited range integer.
|
||||
.item flonum
|
||||
An implementation-dependent limited precision floating point number.
|
||||
.item integer
|
||||
Any integer (no range restriction).
|
||||
.item number
|
||||
Any number.
|
||||
.item notype
|
||||
Unspecified type (i.e., anything else).
|
||||
.end_table
|
||||
|
||||
.chapter "Destructuring"
|
||||
.setq destructuring-section page
|
||||
2Destructuring* provides one with the ability to
|
||||
"simultaneously" assign or bind multiple variables to components of
|
||||
some data structure. Typically this is used with list structure
|
||||
(which is the only mode currently supported). For example,
|
||||
.lisp
|
||||
(desetq (foo . bar) '(a b c))
|
||||
.end_lisp
|
||||
has the effect of setting 3foo* to 3a* and 3bar* to 3(b
|
||||
c)*.
|
||||
LOOP only requires destructuring support when one of these patterns is
|
||||
supplied in place of a variable.
|
||||
In addition, the "binding" of a pattern to a constant 3nil* is so
|
||||
treated that it requires no special support code; this allows the
|
||||
case
|
||||
.lisp
|
||||
with (a b c)
|
||||
.end_lisp
|
||||
to work without destructuring support code.
|
||||
|
||||
One may specify the data types of the components of a pattern
|
||||
by using a corresponding pattern of the data type keywords in place of
|
||||
a single data type keyword. This syntax remains unambiguous because
|
||||
wherever a data type keyword is possible, a 3loop* keyword is
|
||||
the only other possibility. Thus, if one wants to do
|
||||
.lisp
|
||||
(loop for x in l
|
||||
as i fixnum = (car x)
|
||||
and j fixnum = (cadr x)
|
||||
and k fixnum = (cddr x)
|
||||
...)
|
||||
.end_lisp
|
||||
and no reference to 3x* is needed, one may instead write
|
||||
.lisp
|
||||
(loop for (i j . k) (fixnum fixnum . fixnum) in l ...)
|
||||
.end_lisp
|
||||
To allow some abbreviation of the data type pattern, an atomic data
|
||||
type component of the pattern is considered to state that all
|
||||
components of the corresponding part of the variable pattern are of
|
||||
that type. That is, the previous form could be written as
|
||||
.lisp
|
||||
(loop for (i j . k) fixnum in l ...)
|
||||
.end_lisp
|
||||
This generality allows binding of multiple typed variables in a
|
||||
reasonably concise manner, as in
|
||||
.lisp
|
||||
(loop with (a b c) and (i j k) fixnum ...)
|
||||
.end_lisp
|
||||
which binds 3a*, 3b*, and 3c* to 3nil* and 3i*,
|
||||
3j*, and 3k* to 30* for use as temporaries during the
|
||||
iteration, and declares 3i*, 3j*, and 3k* to be fixnums
|
||||
for the benefit of the compiler.
|
||||
.lisp
|
||||
.space 1
|
||||
(defun map-over-properties (fn symbol)
|
||||
(loop for (propname propval) on (plist symbol) by 'cddr
|
||||
do (funcall fn symbol propname propval)))
|
||||
.end_lisp
|
||||
.space 1
|
||||
See also section (dependencies-section),
|
||||
(dependencies-section-page), which discusses support code needed in
|
||||
various implementations.
|
||||
|
||||
.chapter "Iteration Paths"
|
||||
.setq iteration-path-page page
|
||||
Iteration paths provide a mechanism for user extension of
|
||||
iteration-producing clauses. The interface is constrained so that the
|
||||
definition of a path
|
||||
need not depend on much of the internals of LOOP. In general, a path
|
||||
iteration has one of the forms
|
||||
.lisp
|
||||
for 2var* {2data-type*} being 2expr0* and its 2pathname*
|
||||
{2preposition1* 2expr1*}...
|
||||
for 2var* {2data-type*} being {each} 2pathname* of 2expr0*
|
||||
{2preposition1* 2expr1*}
|
||||
.end_lisp
|
||||
The difference between the two is this: in the first, 2var* will
|
||||
take on the value of 2expr0* the first time through the loop; but
|
||||
in the second, it will be the "first step along the path".
|
||||
2Pathname* is an atomic symbol which is defined as a 3loop* path
|
||||
function. The usage and defaulting of 2data-type* is up to the
|
||||
path function. Any number of preposition/expression pairs may be
|
||||
present; the prepositions allowable for any particular path are
|
||||
defined by that path. The 3of* preposition has special
|
||||
meaning in that it specifies the starting point of the path; thus,
|
||||
the first variation shown implicitly uses an 3of* 2expr0*
|
||||
"prepositional phrase". To enhance readability, pathnames are usually
|
||||
defined in both the singular and plural forms. To satisfy the
|
||||
anthropomorphic among you, 3his*, 3her*, or 3their* may be
|
||||
substituted for the 3its* keyword. Egocentricity is not condoned.
|
||||
One pre-defined path is 3cars*; it simply iterates over
|
||||
successive 3car*s of its starting argument, terminating after an
|
||||
atom is reached. For example,
|
||||
.lisp
|
||||
(loop for x being cars of '((a b) c) collect x)
|
||||
=> ((a b) a)
|
||||
(loop for x being '((a b) c) and its cars collect x)
|
||||
=> (((a b) c) (a b) a)
|
||||
.end_lisp
|
||||
The above forms are equivalent to
|
||||
.lisp
|
||||
(loop for x = (car '((a b) c)) then (car x)
|
||||
collect x
|
||||
until (atom x))
|
||||
.end_lisp
|
||||
and
|
||||
.lisp
|
||||
(loop for x = '((a b) c) then (car x)
|
||||
collect x
|
||||
until (atom x))
|
||||
.end_lisp
|
||||
respectively. (Note that the 3atom* check following the
|
||||
body of this loop is part of the
|
||||
definition of the 3cars* path, and is not a property of
|
||||
paths in general.)
|
||||
.group
|
||||
By special dispensation, if a 2pathname* is not
|
||||
recognized, then the 3attachments* path will be invoked upon a
|
||||
syntactic transformation of the original input. This name derives
|
||||
historically from its original usage in XLMS.
|
||||
Essentially, the 3loop* fragment
|
||||
.lisp
|
||||
for 2var* being 2a-r* of 2expr* ...
|
||||
.end_lisp
|
||||
is taken as if it were
|
||||
.lisp
|
||||
for 2var* being attachments in 2a-r-** of 2expr* ...
|
||||
.end_lisp
|
||||
and
|
||||
.lisp
|
||||
for 2var* being 2expr* and its 2a-r* ...
|
||||
.end_lisp
|
||||
is taken as if it were
|
||||
.lisp
|
||||
for 2var* being 2expr* and its attachments in 2a-r-**
|
||||
.end_lisp
|
||||
Thus, this "undefined pathname hook" only works if the
|
||||
3attachments* path is defined. Note also:
|
||||
.defvar loop-attachment-transformer
|
||||
The value of this is a function of one argument which will be called
|
||||
on 2a-r* to transform it into 2a-r-**. If it is
|
||||
3nil*, then a 3quote* is listed around the expression,
|
||||
effectively causing the special 3attachments* syntax to be an
|
||||
unevaluated form of the 3attachments* path. This
|
||||
is initially 3nil* except in an LMS environment, in which case it
|
||||
is a function which simply returns 2a-r*.
|
||||
.end_defvar
|
||||
.end_group
|
||||
|
||||
.need 6000
|
||||
.c 6 inches -- don't want the section header to come out all alone.
|
||||
.section "Defining Paths"
|
||||
|
||||
This section will probably be of interest only to those
|
||||
interested in defining their own paths.
|
||||
For the purposes of discussion, the general template form of
|
||||
an iteration may be assumed to be
|
||||
.lisp
|
||||
(let 2variable-bindings*
|
||||
(prog ()
|
||||
2prologue-code*
|
||||
next-loop
|
||||
2pre-body-endtests-1*
|
||||
2pre-body-steps-1*
|
||||
2pre-body-endtests-2*
|
||||
2pre-body-steps-2*
|
||||
...
|
||||
2body*
|
||||
2post-body-endtests-1*
|
||||
2post-body-steps-1*
|
||||
2post-body-endtests-2*
|
||||
2post-body-steps-2*
|
||||
...
|
||||
(go next-loop)
|
||||
end-loop
|
||||
2epilogue-code*
|
||||
))
|
||||
.end_lisp
|
||||
When more than one 3for* clause is grouped together with 3and*,
|
||||
the endtests and steps are arranged to occur together in parallel.
|
||||
Sequentially arranged 3for* clauses cause multiple endtests and
|
||||
steps to occur one after another, as shown in the above template.
|
||||
A function to generate code for a path may be declared to
|
||||
3loop* with the 3define-loop-path* function:
|
||||
.defun define-loop-path pathname-or-names path-function list-of-allowable-prepositions (1any-number-of*data)
|
||||
This defines 2path-function* to be the handler for the path(s)
|
||||
2pathname-or-names*, which may be either a symbol or a list of
|
||||
symbols. Such a handler should follow the conventions described
|
||||
below.
|
||||
.end_defun
|
||||
|
||||
The handler will be called with the following arguments:
|
||||
.table 2 250 500
|
||||
.item path-name
|
||||
The name of the path which caused the path function to be invoked.
|
||||
.item variable
|
||||
The "iteration variable".
|
||||
.item data-type
|
||||
The data type supplied with the iteration variable, or 3nil* if
|
||||
none was supplied.
|
||||
.item prepositional-phrases
|
||||
This is a list with entries of the form 2(preposition
|
||||
expression)*, in the order in which they were collected. This may
|
||||
also include some supplied implicitly (e.g. 3of* phrases, and
|
||||
3in* phrases for
|
||||
attachment relations); the ordering will show the order of evaluation
|
||||
which should be followed for the expressions.
|
||||
.item inclusive?
|
||||
This is 3t* if 2variable* should have the starting point of
|
||||
the path as its value on the first iteration, 3nil* otherwise.
|
||||
.item allowed-prepositions
|
||||
This is the list of allowable prepositions declared for the pathname
|
||||
that caused the path function to be invoked. It and 2data*
|
||||
(immediately below) may be used by the path function such that a
|
||||
single function may handle similar paths.
|
||||
.item data
|
||||
This is the list of "data" declared for the pathname that caused the
|
||||
path function to be invoked. It may, for instance, contain a
|
||||
canonicalized pathname, or a set of functions or flags to aid the
|
||||
path function in determining what to do. In this way, the same
|
||||
path function may be able to handle different paths.
|
||||
.end_table
|
||||
The handler should return a list with the following elements:
|
||||
.table 2 250 500
|
||||
.item variable-bindings
|
||||
This is a list of variables which need to be bound. The entries in it
|
||||
may be of the form 2variable*, (2variable* 2expression*),
|
||||
or (2variable* 2expression* 2data-type*). Note that it is
|
||||
the responsibility of the handler to make sure the iteration variable
|
||||
gets bound. All of these variables will be bound in parallel; thus,
|
||||
if initialization of one depends on others, it should be done with a
|
||||
3setq* in the 2prologue-forms*.
|
||||
.item prologue-forms
|
||||
This is a list of forms which should be included in the loop prologue.
|
||||
|
||||
.item pre-body-endtest
|
||||
This is a single form.
|
||||
.item pre-body-steps
|
||||
This should be an alternating list of variables and expressions to
|
||||
step them. They will be stepped in parallel. (This is like the
|
||||
arguments to 3setq*; in fact, it will be used as the arguments to
|
||||
3psetq*.)
|
||||
|
||||
.item post-body-endtest
|
||||
Like 2pre-body-endtest*, but done after the 2body*, just
|
||||
before starting the next iteration.
|
||||
.item post-body-steps
|
||||
Like 2pre-body-steps*.
|
||||
.end_table
|
||||
If anyone finds that they need to modify the 2main* body or the
|
||||
epilogue code, we would like to hear about it.
|
||||
A qualification is in order with respect to stepping. In
|
||||
order to make parallel stepping work properly, 3loop* must be able
|
||||
to coerce the stepping code for different 3for* clauses to act in
|
||||
parallel. Thus, the canonical place for stepping to occur is in the
|
||||
2post-body-steps*; the 2pre-body-steps* is mainly useful when
|
||||
the iteration variable needs to be set to some function of whatever is
|
||||
actually being iterated over. For example, the LOOP clause
|
||||
.lisp
|
||||
3for 2var* in 2list**
|
||||
.end_lisp
|
||||
effectively returns the following elements for the template (where
|
||||
2tem* is really a gensymed variable name):
|
||||
.table 2 250 500
|
||||
.item variable-bindings
|
||||
3(2var* (2tem* 2list*))*
|
||||
.item prologue-forms
|
||||
3nil*
|
||||
.item pre-body-endtest
|
||||
3(null 2tem*)*
|
||||
.item pre-body-steps
|
||||
3(2var* (car 2tem*))*
|
||||
.item post-body-endtest
|
||||
3nil*
|
||||
.item post-body-steps
|
||||
3(2tem* (cdr 2tem*))*
|
||||
.end_table
|
||||
|
||||
.defun loop-tequal token symbol-or-string
|
||||
This is the LOOP token comparison function. 2Token* is any Lisp
|
||||
object; 2symbol-or-string* is the keyword it is to be compared
|
||||
against. It returns 3t* if they represent the same token,
|
||||
comparing in a manner appropriate for the implementation. In certain
|
||||
implementations 3loop-tequal* may be a macro.
|
||||
.end_defun
|
||||
|
||||
.chapter "Compatibility with FOR"
|
||||
LOOP is not truly compatible with FOR (a similar Maclisp
|
||||
iteration package). The reason for this is
|
||||
that LOOP has certain "ideas" about how it should handle such things
|
||||
as order of evaluation and repeated evaluation, which are quite
|
||||
different from FOR's simpler template approach. Many of the keywords,
|
||||
and hopefully all of the functionality, have been preserved. In many
|
||||
cases, code written with FOR will work with LOOP, although it
|
||||
sometimes may not behave identically. For convenience, here is a
|
||||
(non-exhaustive) summary of the major differences.
|
||||
One major difference is that LOOP is more fastidious about how
|
||||
it orders the assignments and endtests. Take, for example
|
||||
.lisp
|
||||
(loop for n in list as z = (* n n) collect z)
|
||||
.end_lisp
|
||||
In FOR, 3n* would be assigned to the 3car* of the list, then
|
||||
3z* would be stepped, and then the 3null* check would be made
|
||||
on the iteration list. This means that on the last iteration 3z*
|
||||
will be assigned to 3(* nil nil)*, which might cause some
|
||||
consternation to the Lisp interpreter. In LOOP, first a 3null*
|
||||
check is made on the list, then 3n* is set to the 3car* of the
|
||||
list, then 3z* is stepped.
|
||||
Explicit endtests (3while* and 3until*) are placed
|
||||
"where they appear" in the iteration sequence. This obviates the
|
||||
3repeat-while* and 3repeat-until* keywords of FOR. For
|
||||
example, the FOR construct
|
||||
.lisp
|
||||
(for x in l collect x repeat-while (< x 259.))
|
||||
.end_lisp
|
||||
may be replaced by the LOOP construct
|
||||
.lisp
|
||||
(loop for x in l collect x while (< x 259.))
|
||||
.end_lisp
|
||||
Note that in the FOR case, the ordering of the clauses typically does
|
||||
not matter, but in the LOOP case it typically does. Thus, the
|
||||
ordering in
|
||||
.lisp
|
||||
(loop for data = (generate-some-data)
|
||||
collect (f data)
|
||||
while (test data))
|
||||
.end_lisp
|
||||
causes the result to be a list with at least one element.
|
||||
|
||||
LOOP attempts to suppress repeated evaluation where possible.
|
||||
Which expressions get repeatedly evaluated is documented with the
|
||||
corresponding clauses. One significant example where LOOP and FOR
|
||||
differ is in the case
|
||||
.lisp
|
||||
(loop for i from 0 to 2expression* ...)
|
||||
.end_lisp
|
||||
in which FOR evaluates 2expression* at every iteration, whereas
|
||||
LOOP saves the value at the start of the iteration.
|
||||
It should be noted that the conditionalization clauses
|
||||
(3when*, 3until*, and 3if*) affect only the following
|
||||
clause rather than the whole of the "body" of the iteration, as would
|
||||
be the case in FOR.
|
||||
Because it is difficult for it to work in all cases, the
|
||||
3trailing* clause has been eliminated. Its effect may be achieved,
|
||||
however, by tacking
|
||||
.lisp
|
||||
and 2var* = 2initial-value* then 2var-to-be-trailed*
|
||||
.end_lisp
|
||||
after the 3for* clause which steps 2var-to-be-trailed*.
|
||||
|
||||
.chapter "Dependencies"
|
||||
.setq dependencies-section css-number
|
||||
.setq dependencies-section-page page
|
||||
The LOOP package may require the existence of other routines
|
||||
in some implementations. For efficiency reasons, LOOP avoids
|
||||
producing 3let* in the code it generates unless it is necessary
|
||||
for destructuring bindings.
|
||||
In the PDP-10 Maclisp implementation, LOOP uses 3ferror*
|
||||
to generate error messages; 3ferror* is part of the FORMAT
|
||||
package, and is assumed to be autoloadable from there. 3Let*,
|
||||
which is used to produce destructuring bindings, and the destructuring
|
||||
version of 3setq* called 3desetq*, which is used only when
|
||||
destructuring is used, are both autoloadable. The "parallel setq"
|
||||
mechanism is simulated so that 3psetq* is not needed. Macro
|
||||
memoizing is performed using the same facilities which 3defmacro*
|
||||
uses, and are autoloadable (and typically present in most
|
||||
environments).
|
||||
In Multics Maclisp, LOOP does not presently call 3ferror*,
|
||||
which does not exist.
|
||||
There is a 3let* macro available with
|
||||
destructuring capability; it is non-standard (not part of the Multics
|
||||
Lisp system) -- for further information contact the authors. Currently,
|
||||
macro memoizing is performed by 3rplaca*/3rplacd* splicing,
|
||||
unconditionally.
|
||||
In Lisp Machine lisp, 3ferror* is used to generate errors.
|
||||
This is part of the basic Lisp Machine environment. At this time,
|
||||
destructuring support is not part of the basic environment, although
|
||||
it is available; contact either the authors or the Lisp Machine group
|
||||
if you need this. Macro memoizing is performed using 3displace*,
|
||||
with the same effect as in Multics Maclisp.
|
||||
|
||||
.c Local Modes:
|
||||
.c Auto Fill Mode:1
|
||||
.c Comment Start:.c
|
||||
.c Comment Begin:.c
|
||||
.c Comment End:
|
||||
.eof
|
||||
Reference in New Issue
Block a user