4.17.3 Generators

4.17.1 Sequences
4.17.2 Streams
4.17.3 Generators
On this page:
top
up

4.17.3GeneratorsπŸ”— i

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.

procedure

( generator? v)boolean?

v:any/c
Return #t if v is a generator, #f otherwise.

syntax

( generator formalsbody...+)

formals = (id...)
| (id...+. rest-id)
| rest-id
Creates a generator, where formals specify the arguments. Keyword and optional arguments are not supported. This is the same as the formals of a single case-lambda clause.

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'(abc)])
(if (null? x)
0
(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:contract .

Examples:
> (define my-generator(generator ()(yield 1)(yield 234)))
> (my-generator)

1

> (my-generator)

2

3

4

Examples:
> (define pass-values-generator
(let* ([from-user(yield 2)]
[from-user-again(yield (add1 from-user))])
(yield from-user-again))))
> (pass-values-generator)

2

> (pass-values-generator5)

6

> (pass-values-generator12)

12

syntax

( infinite-generator body...+)

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

Examples:
> (define welcome
(yield 'hello)
(yield 'goodbye)))
> (welcome)

'hello

> (welcome)

'goodbye

> (welcome)

'hello

> (welcome)

'goodbye

syntax

( in-generator maybe-aritybody...+)

maybe-arity =
| #:arityarity-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:
(let loop([x'(abc)])
(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 #:arityarity-k clause; the arity-k must be a literal, exact, non-negative integer.

Examples:
> (let ([g(in-generator
(let loop([n3])
(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 #:arity2
(let loop([n3])
(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'(abc)])
(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
(yield (car x))
(loop(cdr x)))))))
> (for/list ([i(in-producer my-generatormy-stop-value)])
i)

'(a #<void> c)

procedure

( generator-state g)symbol?

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 )

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 )

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
up

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /