2.18 Iterations and Comprehensions: for, for/list, ...
Iterations and Comprehensions in The Racket Guide introduces iterations and comprehensions.
The for iteration forms are based on SRFI-42 [SRFI-42].
2.18.1 Iteration and Comprehension Forms
syntax
(for (for-clause ...) body ...+)
for-clause = [id seq-expr] | [(id ...) seq-expr] | #:when guard-expr | #:unless guard-expr
seq-expr : sequence?
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 or #:unless) determine iteration as above, and the body is effectively wrapped as
(when guard-expr (for (for-clause ...) body ...+))
using the remaining for-clauses. A for-clause of the form #:unless guard-expr corresponds to the same transformation with unless in place of when.
Examples: | ||||||||||||||||||||||||||
|
syntax
(for/list (for-clause ...) body ...+)
Examples: | |||||||||||||
|
syntax
(for/vector (for-clause ...) body ...+)
(for/vector #:length length-expr (for-clause ...) body ...+)
syntax
(for/hash (for-clause ...) body ...+)
syntax
(for/hasheq (for-clause ...) body ...+)
syntax
(for/hasheqv (for-clause ...) body ...+)
Example: | ||||
|
syntax
(for/and (for-clause ...) body ...+)
Examples: | ||||||||||||
|
syntax
(for/or (for-clause ...) body ...+)
Examples: | ||||||||||||
|
syntax
(for/sum (for-clause ...) body ...+)
Example: | ||
|
syntax
(for/product (for-clause ...) body ...+)
Example: | ||
|
syntax
(for/lists (id ...) (for-clause ...) body ...+)
syntax
(for/first (for-clause ...) body ...+)
Examples: | |||||||||
|
syntax
(for/last (for-clause ...) body ...+)
Examples: | |||||||||
|
syntax
(for/fold ([accum-id init-expr] ...) (for-clause ...) . body)
Example: | ||||||||
|
syntax
(for* (for-clause ...) body ...+)
Example: | |||||||
|
syntax
(for*/list (for-clause ...) body ...+)
syntax
(for*/lists (id ...) (for-clause ...) body ...+)
syntax
(for*/vector (for-clause ...) body ...+)
(for*/vector #:length length-expr (for-clause ...) body ...+)
syntax
(for*/hash (for-clause ...) body ...+)
syntax
(for*/hasheq (for-clause ...) body ...+)
syntax
(for*/hasheqv (for-clause ...) body ...+)
syntax
(for*/and (for-clause ...) body ...+)
syntax
(for*/or (for-clause ...) body ...+)
syntax
(for*/sum (for-clause ...) body ...+)
syntax
(for*/product (for-clause ...) body ...+)
syntax
(for*/first (for-clause ...) body ...+)
syntax
(for*/last (for-clause ...) body ...+)
syntax
(for*/fold ([accum-id init-expr] ...) (for-clause ...) body ...+)
Example: | |||||
|
2.18.2 Deriving New Iteration Forms
syntax
(for/fold/derived orig-datum ([accum-id init-expr] ...) (for-clause ...) body ...+)
Examples: | ||||||||||||||||||||||||||||
|
syntax
(for*/fold/derived orig-datum ([accum-id init-expr] ...) (for-clause ...) body ...+)
Examples: | ||||||||||||||||||||||||||||
|
syntax
(define-sequence-syntax id expr-transform-expr clause-transform-expr)
expr-transform-expr :
(or/c (-> identifier?) (syntax? . -> . syntax?))
clause-transform-expr : (syntax? . -> . syntax?)
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 replace the given one. The new clause might use :do-in. To protect identifiers in the result of clause-transform-expr, use for-clause-syntax-protect instead of syntax-protect.
Examples: | ||||||||||||||||||||||||||||
|
syntax
(:do-in ([(outer-id ...) outer-expr] ...) outer-check ([loop-id loop-expr] ...) pos-guard ([(inner-id ...) inner-expr] ...) pre-guard post-guard (loop-arg ...))
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 from parallel iterations.
For an example of :do-in, see define-sequence-syntax.
procedure
(for-clause-syntax-protect stx) → syntax?
stx : syntax?
Use this function to protect the result of a clause-transform-expr that is bound by define-sequence-syntax.
2.18.3 Do Loops
syntax
(do ([id init-expr step-expr-maybe] ...) (stop?-expr finish-expr ...) expr ...)
step-expr-maybe =
| step-expr
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 the ids have been bound, the stop?-expr is evaluated. If it produces #f, each expr is evaluated for its side-effect. The ids are then effectively updated with the values of the step-exprs, where the default step-expr for id is just id; more precisely, iteration continues with fresh locations for the ids that are initialized with the values of the corresponding step-exprs.
When stop?-expr produces a true value, 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>.