8Generators

There is no doubt that lists are useful structures for representing many kinds of data, and that folds and maps are a quick, convenient way of performing arbitrary bits of list manipulation.

The main problem with the list/fold/map approach is the number of temporary lists generated in the process, which can take up a large amount of memory.

Generators are a half-way-house between lists and streams that aim to reduce memory overhead when large data sources are involved.

A generator is a stream-like accessor that can be repeatedly called to return new values from its source. A special "generator-end" value is returned to indicate that the source has been exhausted.

This module provides convenient ways of:

producing generators from lists;

combining generators to form other generators (c.f. fold, map and so on);

accumulating results from generators (e.g. back into lists).

Many of the procedures defined in this module have rather unwieldy names. "gen.ss" exports versions of these procedures with shorter names: see Generators (short names) for more information.

generator-end:symbol?

A unique symbol that indicates a generator has finished producing values.

(generator-end?item)→boolean?

item:any

Predicate that recognizes generator-end.

(gen->value-contract)→flat-contract?

value-contract:flat-contract?

Syntax that expands into a contract that is satisfied by generator procedures that produce values that satisfy value-contract or generator-end?.

(list->generatorlis)→(gen->a)

lis:(listofa)

Creates a generator that generates the values in lis.

Examples:

> (definegen

(list->generator'(12)))

> (gen)

1

> (gen)

2

> (gen)

generator-end2691

(range->generatorstart[endstep])→(gen->integer?)

start:integer?

end:(Uinteger?#f)=#f

step:integer?=1

Creates a generator that generates the values in the range of integers from start (inclusive) to end (exclusive), incrementing each time by step.

The generator stops when the current value passes end in the direction determined by step. end itself is considered a terminating value.

Examples:

> (definegen

(range->generator152))

> (gen)

1

> (gen)

3

> (gen)

generator-end2691

(generator-mapfngen1gen2...)→(gen->ans)

fn:(arg1arg2...->ans)

gen1:(gen->arg1)

gen2:(gen->arg2)

The generator equivalent of map. Creates a generator that returns:

(applyfn(list(gen1)(gen2)...))

The new generator ends as soon as any of gen1gen2... end.

Examples:

> (definegen

(generator-map+

(list->generator'(12))

(list->generator'(23))))

> (gen)

3

> (gen)

5

> (gen)

generator-end2691

(generator-fold-map

fn

initial-seed

gen1

gen2...)

→

seed

fn:(arg1arg2...seed->seed)

initial-seed:seed

gen1:(gen->arg1)

gen2:(gen->arg2)

A generator equivalent of fold. The new generator returns the values of seed for each application of fn, stopping when any of the source generators stop.

Examples:

> (definegen

(generator-fold-map+0(list->generator'(12))))

> (gen)

1

> (gen)

3

> (gen)

generator-end2691

(generator-filterpredsrc)→(gen->arg)

pred:(arg->boolean?)

src:(gen->arg)

Creates a generator that generates the values from src for which pred returns non-#f.

Examples:

> (definegen

(generator-filtereven?(list->generator'(12345))))

> (gen)

2

> (gen)

4

> (gen)

generator-end2691

(generator-filter-mapfnsrc)→(gen->ans)

fn:(arg->(Uans#f))

src:(gen->arg)

Creates a generator that generates the non-#f values of (fn(src)).

Examples:

> (define(testval)

(memqval'(246)))

> (definegen

(generator-filter-maptest(list->generator'(12345))))

> (gen)

(246)

> (gen)

(46)

> (gen)

generator-end2691

(generator-remove-duplicatessrc[same?])→(gen->a)

src:(gen->a)

same?:(aa->boolean?)=equal?

Creates a generator that filters out values from src that occur more than once in succession. The optional argument same? is used to test equality.

Examples:

> (definegen

(generator-remove-duplicates(list->generator'(122333))))

> (gen)

1

> (gen)

2

> (gen)

3

> (gen)

generator-end2691

(generator-for-eachfngen1gen2...)→void?

fn:(arg1arg2...->void)

gen1:(gen->arg1)

gen2:(gen->arg2)

Repeatedly applies fn for its side-effects to values form source generators gen1gen2... until one or more sources is exhausted.

Examples:

> (generator-for-eachdisplay(list->generator'(123)))

123

(generator-foldfninitial-seedgen1gen2...)→seed

fn:(arg1arg2...seed->seed)

initial-seed:seed

gen1:(gen->arg1)

gen2:(gen->arg2)

Like generator-fold-map but only the result of the final application of fn is returned.

Examples:

> (generator-fold+0(list->generator'(123)))

6

(generator->listsrc)→(listofa)

src:(gen->a)

A convenience form of generator-fold that collects the generated
values into a list (in the order generated).

Examples:

> (generator->list(list->generator'(123)))

(123)

(generator->hash

src

item->key

[

item->val

initial-hash])

→

(hashofbc)

src:(gen->a)

item->key:(a->b)

item->val:(a->c)=(lambda(a)a)

initial-hash:(hashofbc)=(make-hash)

Similar to generator->list but collects the generated values into a hash table. item->key an item->val control how generated items are turned into keys and values in the hash. initial-hash lets you specify the (mutable) hash table to use for the collection (useful for switching to a hash table that uses eq? as a comparison function).

Examples:

> (hash-map(generator->hash(list->generator'(246))(lambda(x)(/x2)))

(lambda(keyval)

(listkeyval)))

((24)(12)(36))

(generator-projectmasksrc[same?])

→(gen->(listany...(listof(listofany))))

mask:(listofboolean)

src:(gen->(listofany))

same?:[(anyany->boolean)]=eq?

Does the equivalent of a projection (from relational algebra) on the values returned by src. #t values in mask correspond (by position) to "key" values in values from src, while #f values correspond to "nonkey" values.

src is polled once and the members returned are partitioned into keys and nonkeys and stored. src is then polled repeatedly until it returns a list where the keys differ from those stored. At this point, the generator emits a list of the matching keys and a list of the lists of nonkeys:

(listkey...(listof(listnonkey...)))

The optional same? argument is the predicate used to check key equality.

generator-project is useful (in conjunction with generator-map, map and match-lambda) for iterating over sets of related results returned by database queries.