The basic for loop has two mandatory ingredients: an iterator binding, consisting of an identifier and a sequence of values, and a body of one or more expressions. On each pass of the loop, the next value from the sequence is assigned to the identifier, and the body of the loop is evaluated. The loop exits when the iterator runs out of values:
1 2 3 | 1 2 3 |
A loop can have more than one iterator binding. In that case, the iterators are assigned in parallel, and the loop exits when any iterator runs out of values:
1 2 | '(1 1) '(2 2) |
A for loop discards the results of evaluating the body. Thus, it’s mostly useful for functions that rely on side effects, like println. But as an expression, it evaluates to void:
By contrast, the for/list and for/vector variants don’t discard the results of evaluating the body. Instead, they gather these values into a new list or vector:
for/hash follows the same idea, but expects the body to return two values on each pass, representing a key–value pair:
for/fold is the most flexible of these variants. It allows any number of accumulators, of any type, and expects the body to return the same number of values on each pass:
1 2 3 | '(1 2 3) ; list of integers '#(1 4 9) ; vector of squares '#hash((1 . 1) (3 . 27) (2 . 8)) ; hash of cubes |
See the Racket documentation for details on other convenience variants like for/sum, for/product, for/and, for/or, and for/first.
Any value that counts as a sequence—including positive integers, lists, and strings—can be used directly as a source of iteration values in any loop:
Note that a string used as a sequence becomes a series of raw characters, not substrings. For that, use a list of one-character strings as the sequence:
1 | (for/list ([s (regexp-match* #rx"." "bar")]) s) ; '("b" "a" "r") |
Though a sequence can be used directly as the right side of an iterator binding, the best performance comes from using sequence constructors like in-range, in-list, and in-string: + Using a constructor tips off the compiler about what kind of sequence will be in the iterator position, so access to the sequence can be optimized at compile time.
Specialized constructors handle other sequence types, like in-hash, in-port, and in-directory.
The in-naturals constructor creates an unending stream of integers. Use this if you need an infinite loop:
1 2 | (for ([i (in-naturals)]) (displayln (format "~a bottles of beer on the wall" i))) |
A guard expression limits the evaluation of the body based on a condition. Possibilities include #:when or its inverse, #:unless:
A break expression can be used to exit the loop early based on a condition. Possibilities include #:break, which exits the loop immediately, and #:final, which allows one more evaluation of the body before exiting:
1 2 3 4 5 6 | (for/list ([i (in-naturals)] #:break (= i 3)) i) ; '(0 1 2) (for/list ([i (in-naturals)] #:final (= i 3)) i) ; '(0 1 2 3) |
By default, a loop with multiple iterators assigns them in parallel. To assign iterators in a nested way, use the for* variant of any loop form (so the nested version of for/list is called for*/list):
map and filter are the idiomatic functions for performing operations on lists. Still, it can sometimes be more readable to put these operations in terms of for/list. For instance, these two expressions make the same list:
Iterations & Comprehensions in the Racket Guide
Iterations & Comprehensions in the Racket Reference
Sequences in the Racket Reference