4.14.3 Generators

► |  | The Racket Reference

1 | Language Model
2 | Notation for Documentation
3 | Syntactic Forms
4 | Datatypes
5 | Structures
6 | Classes and Objects
7 | Units
8 | Contracts
9 | Pattern Matching
10 | Control Flow
11 | Concurrency and Parallelism
12 | Macros
13 | Input and Output
14 | Reflection and Security
15 | Operating System
16 | Memory Management
17 | Unsafe Operations
18 | Running Racket
| Bibliography
| Index

► | 4 | Datatypes

4.1 | Booleans and Equality
4.2 | Numbers
4.3 | Strings
4.4 | Byte Strings
4.5 | Characters
4.6 | Symbols
4.7 | Regular Expressions
4.8 | Keywords
4.9 | Pairs and Lists
4.10 | Mutable Pairs and Lists
4.11 | Vectors
4.12 | Boxes
4.13 | Hash Tables
4.14 | Sequences and Streams
4.15 | Dictionaries
4.16 | Sets
4.17 | Procedures
4.18 | Void
4.19 | Undefined

▼ | 4.14 | Sequences and Streams

4.14.1 | Sequences
4.14.2 | Streams
4.14.3 | Generators

On this page:

generator?
generator
yield
infinite-generator
in-generator
generator-state
sequence->generator
sequence->repeated-generator

top ← prev up next →

##### 4.14.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

Return #t if v is a generator, #f otherwise.

> syntax (generator formals body ...+)

Creates a generator, where formals is like the formals of case-lambda (i.e., the kw-formals of lambda restricted to non-optional and non-keyword arguments).

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.

Examples:

> > (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

> procedure (yield v ...) → any
> v : any/c

Returns vs from a generator, saving the point of execution inside a generator (i.e., within the dynamic extent of a generator body) to be resumed by the next call to the generator. The results of yield are the arguments that are provided to the next call of the generator.

When not in the dynamic extent of a generator, infinite-generator, or in-generator body, yield raises exn:fail after evaluating its exprs.

Examples:

> > (define my-generator (generator () (yield 1) (yield 2 3 4)))
> > (my-generator)
> 1
> > (my-generator)
> 2
> 3
> 4

Examples:

> > (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 ...+)

Like generator, but repeats evaluation of the bodys when the last body completes without implicitly yielding.

Examples:

> > (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

Produces a sequence that encapsulates the generator formed by (generator () body ...+). The values produced by the generator form the elements of the sequence, except for the last value produced by the generator (i.e., the values produced by returning).

Example:

> > (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.

Examples:

> > (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
> arguments...:
> 3
> 4
> > (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?

Returns a symbol that describes the state of the 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.

Examples:

> > (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?

Converts a sequence to a generator. The generator returns the next element of the sequence each time the generator is invoked, where each element of the sequence must be a single value. When the sequence ends, the generator returns #<void> as its final result.

> procedure (sequence->repeated-generator s) → (-> any)
> s : sequence?

Like sequence->generator, but when s has no further values, the generator starts the sequence again (so that the generator never stops producing values).
top ← prev up next →