4.17.3 Generators
A generator is a procedure that returns a sequence of values, incrementing the sequence each time that the generator is called. In particular, the generator form implements a generator by evaluating a body that calls yield to return values from the generator.
(require racket/generator) | package: base |
procedure
(generator? v) → boolean?
v : any/c
syntax
(generator formals body ...+)
formals = (id ...) | (id ...+ . rest-id) | rest-id
For the first call to a generator, the arguments are bound to the formals and evaluation of body starts. During the dynamic extent of body, the generator can return immediately using the yield function. A second call to the generator resumes at the yield call, producing the arguments of the second call as the results of the yield, and so on. The eventual results of body are supplied to an implicit final yield; after that final yield, calling the generator again returns the same values, but all such calls must provide 0 arguments to the generator.
> (define g (generator () (let loop ([x '(a b c)]) (if (null? x) 0 (begin (yield (car x)) (loop (cdr x))))))) > (g) 'a
> (g) 'b
> (g) 'c
> (g) 0
> (g) 0
When not in the dynamic extent of a generator, infinite-generator, or in-generator body, yield raises exn:fail:contract.
> (define pass-values-generator (generator () (let* ([from-user (yield 2)] [from-user-again (yield (add1 from-user))]) (yield from-user-again)))) > (pass-values-generator) 2
> (pass-values-generator 5) 6
> (pass-values-generator 12) 12
syntax
(infinite-generator body ...+)
> (define welcome (infinite-generator (yield 'hello) (yield 'goodbye))) > (welcome) 'hello
> (welcome) 'goodbye
> (welcome) 'hello
> (welcome) 'goodbye
syntax
(in-generator maybe-arity body ...+)
maybe-arity =
| #:arity arity-k
> (for/list ([i (in-generator (let loop ([x '(a b c)]) (when (not (null? x)) (yield (car x)) (loop (cdr x)))))]) i) '(a b c)
If in-generator is used immediately with a for (or for/list, etc.) binding’s right-hand side, then its result arity (i.e., the number of values in each element of the sequence) can be inferred. Otherwise, if the generator produces multiple values for each element, its arity should be declared with an #:arity arity-k clause; the arity-k must be a literal, exact, non-negative integer.
> (let ([g (in-generator (let loop ([n 3]) (unless (zero? n) (yield n (add1 n)) (loop (sub1 n)))))]) (let-values ([(not-empty? next) (sequence-generate g)]) (let loop () (when (not-empty?) (next) (loop))) 'done)) stop?: arity mismatch;
the expected number of arguments does not match the given
number
expected: 1
given: 2
> (let ([g (in-generator #:arity 2 (let loop ([n 3]) (unless (zero? n) (yield n (add1 n)) (loop (sub1 n)))))]) (let-values ([(not-empty? next) (sequence-generate g)]) (let loop () (when (not-empty?) (next) (loop))) 'done)) 'done
To use an existing generator as a sequence, use in-producer with a stop-value known for the generator:
> (define abc-generator (generator () (for ([x '(a b c)]) (yield x))))
> (for/list ([i (in-producer abc-generator (void))]) i) '(a b c)
> (define my-stop-value (gensym))
> (define my-generator (generator () (let loop ([x (list 'a (void) 'c)]) (if (null? x) my-stop-value (begin (yield (car x)) (loop (cdr x)))))))
> (for/list ([i (in-producer my-generator my-stop-value)]) i) '(a #<void> c)
procedure
(generator-state g) → symbol?
g : generator?
'fresh —
The generator has been freshly created and has not been called yet. 'suspended —
Control within the generator has been suspended due to a call to yield. The generator can be called. 'running —
The generator is currently executing. 'done —
The generator has executed its entire body and will continue to produce the same result as from the last call.
> (define my-generator (generator () (yield 1) (yield 2))) > (generator-state my-generator) 'fresh
> (my-generator) 1
> (generator-state my-generator) 'suspended
> (my-generator) 2
> (generator-state my-generator) 'suspended
> (my-generator) > (generator-state my-generator) 'done
> (define introspective-generator (generator () ((yield 1)))) > (introspective-generator) 1
> (introspective-generator (lambda () (generator-state introspective-generator))) 'running
> (generator-state introspective-generator) 'done
> (introspective-generator) 'running
procedure
(sequence->generator s) → (-> any)
s : sequence?
procedure
(sequence->repeated-generator s) → (-> any)
s : sequence?