Siddhartha Kasivajhula
Primitives and utilities for working with generators.
This module provides general-purpose utilities to achieve standard "list-like" transformations with generators without losing the laziness and constant-memory guarantees that generators provide. Alternative bindings for many of those found in racket/generator are included here, so that importing both is generally not necessary.
This module additionally provides a generic interface gen:generator representing a generator, as well as a particular generator type gen implementing that interface. The former allows supporting generator semantics in custom types and the latter is a rich generator type usable as a drop-in replacement for built-in generators, which comes with some conveniences.
Caveat: These utilities are not suitable for use with coroutines, i.e. in cases where there is bidirectional communication with a generator. This is because the utilities wrap underlying generators with intermediary ones in some cases, and values sent to them are not conveyed to the underlying generators.
primitive:generator?
> (g2)3
> (g2)4
> (g2)5
> (g2)1
> (g2)2
procedure
( generator-null [return])→generator?
procedure
( make-generator v...)→generator?
v:any/c
Note that these constructors are not lazy. In order to construct a generator from a sequence lazily, use generate instead.
These constructors could either be used directly or via the generic construction operator : – see gen for examples of this.
> (g)1
#t
> (g)1
> (g)23
> (g)1
'(2 3)
syntax
( generator formalsbody...)
procedure
( generator-done? g)→boolean?
procedure
generator-empty? returns both a boolean value indicating whether the generator is empty or not, as well as a fresh generator intended to supplant the original generator in the calling context. This is necessary because checking for emptiness requires invoking the generator to inspect the first element, which mutates the original generator. The returned generator is equivalent to the original generator prior to the mutation, modulo the aforementioned caveat about coroutines.
In general, favor using generator-done? .
#f
> is-empty?#t
#f
> (g)#t
procedure
> v1
> (g)1
> (g)2
> (g)3
procedure
( generator-map fg)→generator?
> (g)2
> (g)3
> (g)4
procedure
( generator-filter fg)→generator?
> (g)1
> (g)3
> (g)5
procedure
( generator-fold fg[base#:orderorder])→generator?
> (g)1
> (g)3
> (g)6
> (g)10
procedure
( generator-append ab)→generator?
> (g)1
> (g)2
> (g)3
> (g)4
procedure
( generator-cycle g[stop])→generator?
> (g)1
> (g)2
> (g)3
> (g)1
> (g)2
procedure
v:any/c
> (g)5
> (g)5
> (g)5
procedure
( generator-zip g...)→generator?
> (g)'(1 a A)
> (g)'(2 b B)
> (g)3
> (g)6
procedure
( generator-interleave g...)→generator?
> (g)1
> (g)4
> (g)2
> (g)5
> (g)3
> (g)6
procedure
( generator-join g)→generator?
> (g)1
> (g)2
> (g)3
procedure
> (g)1
> (g)2
> (g)3
procedure
( yield-from g)→any
> (g)1
> (g)2
> (g)3
procedure
( generate seq[return])→generator?
seq:sequence?
To go in the "other direction" and transform a generator into a sequence, use in-producer .
> (g)5
> (g)6
> (g)7
> (g)#\a
> (g)#\p
> (g)#\p
procedure
( in-producer g[stop]v...)→sequence?
v:any/c
To go in the "other direction" and transform a sequence into a generator, use generate .
'(1 2 3)
value
#:transparent#:propertyprop:procedure((api-reader-sourceself)))#:methodsgen:generator(-generator-state(api-reader-sourcesource)))])> (g)1
'(2 3)
To implement this interface for custom types, the following method needs to be implemented:
procedure
( generator-state v)
procedure
( generator? v)→boolean?
v:any/c
#f
#t
#t