2.18 Iterations and Comprehensions: for, for/list, ...
Iterations and Comprehensions in Guide: PLT Scheme introduces iterations and comprehensions.
The for iteration forms are based on SRFI-42 [SRFI-42].
2.18.1 Iteration and Comprehension Forms
(for (for-clause ) body ) | |||||||||||||||
| |||||||||||||||
|
Iteratively evaluates body. The for-clauses introduce bindings whose scope includes body and that determine the number of times that body is evaluated.
In the simple case, each for-clause has one of its first two forms, where [id seq-expr] is a shorthand for [(id ) seq-expr]. In this simple case, the seq-exprs are evaluated left-to-right, and each must produce a sequence value (see Sequences).
The for form iterates by drawing an element from each sequence; if any sequence is empty, then the iteration stops, and #<void> is the result of the for expression. Otherwise a location is created for each id to hold the values of each element; the sequence produced by a seq-expr must return as many values for each iteration as corresponding ids.
The ids are then bound in the body, which is evaluated, and whose results are ignored. Iteration continues with the next element in each sequence and with fresh locations for each id.
A for form with zero for-clauses is equivalent to a single for-clause that binds an unreferenced id to a sequence containing a single element. All of the ids must be distinct according to bound-identifier=?.
If any for-clause has the form #:when guard-expr, then only the preceding clauses (containing no #:when) determine iteration as above, and the body is effectively wrapped as
(when guard-expr |
(for (for-clause ) body )) |
using the remaining for-clauses.
Examples: | |||||
| |||||
(1 a #t)(1 a #f)(3 c #t)(3 c #f) | |||||
| |||||
(a 1)(b 20) | |||||
| |||||
here | |||||
|
(for/list (for-clause ) body ) |
Iterates like for, but that the last expression in the bodys must produce a single value, and the result of the for/list expression is a list of the results in order.
Examples: | |||||
| |||||
((1 #\a #t) (1 #\a #f) (3 #\c #t) (3 #\c #f)) | |||||
> (for/list () 'any) | |||||
(any) | |||||
| |||||
() |
| |
|
Like for/list, but the result is an immutable hash table; for/hash creates a table using equal? to distinguish keys, and for/hasheq produces a table using eq?. The last expression in the bodys must return two values: a key and a value to extend the hash table accumulated by the iteration.
Examples: | ||
| ||
#hash((1 . "1") (2 . "2") (3 . "3")) |
(for/and (for-clause ) body ) |
Iterates like for, but when last expression of body produces #f, then iteration terminates, and the result of the for/and expression is #f. If the body is never evaluated, then the result of the for/and expression is #t. Otherwise, the result is the (single) result from the last evaluation of body.
Examples: | ||
| ||
#f | ||
| ||
4 | ||
| ||
#t |
(for/or (for-clause ) body ) |
Iterates like for, but when last expression of body produces a value other than #f, then iteration terminates, and the result of the for/or expression is the same (single) value. If the body is never evaluated, then the result of the for/or expression is #f. Otherwise, the result is #f.
Examples: | ||
| ||
#t | ||
| ||
1 | ||
| ||
#f |
(for/lists (id ) (for-clause ) body ) |
Similar to for/list, but the last body expression should produce as many values as given ids, and the result is as many lists as supplied ids. The ids are bound to the lists accumulated so far in the for-clauses and bodys.
(for/first (for-clause ) body ) |
Iterates like for, but after body is evaluated the first time, then the iteration terminates, and the for/first result is the (single) result of body. If the body is never evaluated, then the result of the for/first expression is #f.
Examples: | |||
| |||
"2" | |||
| |||
#f |
(for/last (for-clause ) body ) |
Iterates like for, but the for/last result is the (single) result of of the last evaluation of body. If the body is never evaluated, then the result of the for/last expression is #f.
Examples: | |||
| |||
"4" | |||
| |||
#f |
(for/fold ([accum-id init-expr] ) (for-clause ) . body) |
Iterates like for. Before iteration starts, the init-exprs are evaluated to produce initial accumulator values. At the start of each out iteration, a location is generated for each accum-id, and the correspinding current accumulator value is placed into the location. The last expression in body must produce as many values as accum-ids, and those values become the current accumulator values. When iteration terminates, the results of the fold/for expression are the accumulator values.
Examples: | ||||
| ||||
10 | ||||
(2 1.7320508075688772 1.4142135623730951 1) |
(for* (for-clause ) body ) |
Like for, but with an implicit #:when #t between each pair of for-clauses, so that all sequence iterations are nested.
Examples: | |||
| |||
(1 a)(1 b)(2 a)(2 b) |
| |
| |
| |
| |
| |
| |
| |
| |
|
Like for/list, etc., but with the implicit nesting of for*.
Examples: | |||
| |||
((1 #\a) (1 #\b) (2 #\a) (2 #\b)) |
2.18.2 Deriving New Iteration Forms
|
Like for/fold, but the extra orig-datum is used as the source for all syntax errors.
|
Like for*/fold, but the extra orig-datum is used as the source for all syntax errors.
|
Defines id as syntax. An (id . rest) form is treated specially when used to generate a sequence in a clause of for (or one of its variants). In that case, the procedure result of clause-transform-expr is called to transform the clause.
When id is used in any other expression position, the result of expr-transform-expr is used. If it is a procedure of zero arguments, then the result must be an identifier other-id, and any use of id is converted to a use of other-id. Otherwise,expr-transform-expr must produce a procedure (of one argument) that is used as a macro transformer.
When the clause-transform-expr transformer is used, it is given a clause as an argument, where the clause’s form is normalized so that the left-hand side is a parenthesized sequence of identifiers. The right-hand side is of the form (id . rest). The result can be either #f, to indicate that the forms should not be treated specially (perhaps because the number of bound identifiers is inconsistent with the (id . rest) form), or a new clause to to replace the given one. The new clause might use :do-in.
|
A form that can only be used as a seq-expr in a clause of for (or one of its variants).
Within a for, the pieces of the :do-in form are spliced into the iteration essentially as follows:
(let-values ([(outer-id ) outer-expr] ) |
outer-check |
(let loop ([loop-id loop-expr] ) |
(if pos-guard |
(let-values ([(inner-id ) inner-expr] ) |
(if pre-guard |
(let body-bindings |
(if post-guard |
(loop loop-arg ) |
done-expr)) |
done-expr)) |
done-expr))) |
where body-bindings and done-expr are from the context of the :do-in use. The identifiers bound by the for clause are typically part of the ([(inner-id ) inner-expr] ) section.
The actual loop binding and call has additional loop arguments to support iterations in parallel with the :do-in form, and the other pieces are similarly accompanied by pieces form parallel iterations.
2.18.3 Do Loops
| ||||||||||
| ||||||||||
|
Iteratively evaluates the exprs for as long as cont-expr? returns #t.
To initialize the loop, the init-exprs are evaluated in order and bound to the corresponding ids. The ids are bound in all expressions within the form other than the init-exprs.
After he ids are bound, then cont?-expr is evaluated. If it produces a true value, then each expr is evaluated for its side-effect. The ids are then updated with the values of the step-exprs, where the default step-expr for id is just id. Iteration continues by evaluating cont?-expr.
When cont?-expr produces #f, then the finish-exprs are evaluated in order, and the last one is evaluated in tail position to produce the overall value for the do form. If no finish-expr is provided, the value of the do form is #<void>.