mirror of
https://github.com/justinethier/cyclone.git
synced 2025-05-18 21:29:18 +02:00
455 lines
16 KiB
Markdown
455 lines
16 KiB
Markdown
# SRFI 121 - Generators
|
|
|
|
Defines utility procedures that create, transform and consume generators. A
|
|
'generator' is a procedure with no arguments (a 'thunk') that works as a source
|
|
of values. Every time a generator is called, it yields a value.
|
|
|
|
Generators may be finite or infinite; a finite generator returns an end-of-file
|
|
object to indicate that it is exhausted (has no more values to give). For
|
|
example, ``read-char``, ``read-line`` and ``read`` are generators that produce
|
|
characters, lines and objects from the current input port.
|
|
|
|
This library is designed to provide lightweight laziness.
|
|
|
|
See the [SRFI document][http://srfi.schemers.org/srfi-121/srfi-121.html] for more information.
|
|
|
|
## Definitions
|
|
|
|
Generators can be divided into two classes: *finite* and *infinite*. Both kinds
|
|
of generator can be invoked an indefinite number of times. After a finite
|
|
generator has produced all of its values, it will return an end-of-file object
|
|
for all subsequent calls. A generator is said to be *exhausted* if calling it
|
|
will return an end-of-file object. By definition, an infinite generator can
|
|
never be exhausted.
|
|
|
|
A generator is said to be in an *undefined state* if it cannot be determined how
|
|
many values it has produced. This arises because it is impossible to tell by
|
|
inspection whether a generator is exhausted or not. For example,
|
|
``(generator-fold + 0 (generator 1 2 3) (generator 1 2))`` will compute 0 + 1 +
|
|
1 + 2 + 2 = 6, at which time the second generator will be exhausted. If the
|
|
first generator is invoked, however, it may return either 3 or an end-of-file
|
|
object, depending on whether the implementation of ``generator-fold`` invoked it
|
|
or not. Therefore, the first generator is said to be in an undefined state.
|
|
|
|
Functions provided under [generator operations](#generator-operations) do not
|
|
consume elements from their input generators. In general, they produce finite
|
|
generators if their inputs are finite.
|
|
|
|
Functions provided udner [consuming generated
|
|
values](#consuming-generated-values) consume all values from any generator
|
|
passed to them, and will not return if any of their arguments are infinite.
|
|
|
|
## Generator constructors
|
|
[`generator`](#generator)
|
|
[`make-iota-generator`](#make-iota-generator)
|
|
[`make-range-generator`](#make-range-generator)
|
|
[`make-coroutine-generator`](#make-coroutine-generator)
|
|
[`list->generator`](#list-generator)
|
|
[`vector->generator`](#vector-generator)
|
|
[`reverse-vector->generator`](#reverse-vector-generator)
|
|
[`string->generator`](#string-generator)
|
|
[`bytevector->generator`](#bytevector-generator)
|
|
[`make-for-each-generator`](#make-for-each-generator)
|
|
[`make-unfold-generator`](#make-unfold-generator)
|
|
|
|
## Generator operations
|
|
[`gcons*`](#gcons)
|
|
[`gappend`](#gappend)
|
|
[`gcombine`](#gcombine)
|
|
[`gfilter`](#gfilter)
|
|
[`gremove`](#gremove)
|
|
[`gtake`](#gtake)
|
|
[`gdrop`](#gdrop)
|
|
[`gtake-while`](#gtake-while)
|
|
[`gdrop-while`](#gdrop-while)
|
|
[`gdelete`](#gdelete)
|
|
[`gdelete-neighbor-dups`](#gdelete-neighbor-dups)
|
|
[`gindex`](#gindex)
|
|
[`gselect`](#gselect)
|
|
|
|
## Consuming generated values
|
|
[`generator->list`](#generator-list)
|
|
[`generator->reverse-list`](#generator-reverse-list)
|
|
[`generator->vector`](#generator-vector)
|
|
[`generator->vector!`](#generator-vector!)
|
|
[`generator->string`](#generator-string)
|
|
[`generator-fold`](#generator-fold)
|
|
[`generator-for-each`](#generator-for-each)
|
|
[`generator-find`](#generator-find)
|
|
[`generator-count`](#generator-count)
|
|
[`generator-any`](#generator-any)
|
|
[`generator-every`](#generator-every)
|
|
[`generator-unfold`](#generator-unfold)
|
|
|
|
# generator
|
|
|
|
(generator arg ...)
|
|
|
|
Returns a generator which produces each of this function's arguments in turn.
|
|
When given no arguments, returns an empty generator which provides no values.
|
|
|
|
# make-iota-generator
|
|
|
|
(make-iota-generator count)
|
|
(make-iota-generator count start)
|
|
(make-iota-generator count start step)
|
|
|
|
Returns a finite generator which produces a sequence of ``count`` numbers. The
|
|
sequence begins with ``start`` (default 0) and increases by ``step`` (default
|
|
1). If both ``start`` and ``step`` are exact, the generator produces exact
|
|
values; otherwise, it produces inexact ones. The exactness of ``count`` does not
|
|
affect the exactness of results.
|
|
|
|
Example: ``(generator->list (make-iota-generator 3 8))`` => (8 9 10)``
|
|
|
|
# make-range-generator
|
|
|
|
(make-range-generator start)
|
|
(make-range-generator start end)
|
|
(make-range-generator start end step)
|
|
|
|
Returns a generator which produces a sequence of numbers. The sequence begins
|
|
with ``start``, increases by ``step`` (default 1), and continues while the
|
|
number is less than ``end``, or forever if ``end`` is not provided. If both
|
|
``start`` and ``step`` are exact, the generator produces exact values;
|
|
otherwise, it produces inexact ones. The exactness of ``end`` does not affect
|
|
the exactness of the results.
|
|
|
|
Example: ``(generator->list (make-range-generator 3) 4) => (3 4 5 6)``
|
|
|
|
# make-coroutine-generator
|
|
|
|
(make-coroutine-generator proc)
|
|
|
|
Creates a generator from a coroutine. The ``proc`` argument should be a
|
|
procedure that takes a single argument ``yield``. When called,
|
|
``make-coroutine-generator`` immediately returns a generator ``g``. When ``g``
|
|
is called, ``proc`` runs until it calls ``yield``. Calling ``yield`` causes the
|
|
execution of ``proc`` to be suspended, and ``g`` returns the value passed to
|
|
``yield``.
|
|
|
|
Whether ``g`` is finite or infinite depends on the behaviour of ``proc``: if
|
|
``proc`` returns, it is the end of the sequence, and ``g`` will return an
|
|
end-of-file object from then on. The return value of ``proc`` is ignored.
|
|
|
|
# list->generator
|
|
|
|
(list->generator lis)
|
|
|
|
Returns a generator that produces each element of the list ``lis`` in turn.
|
|
Mutating ``lis`` will affect the results of the generator.
|
|
|
|
``list->generator`` and ``generator->list`` (when given no arguments) are
|
|
inverses up to ``equal?``; thus, for any list ``x``,
|
|
``(equal? x (generator->list (list->generator x))) => #t``.
|
|
|
|
# vector->generator
|
|
|
|
(vector->generator vec)
|
|
(vector->generator vec start)
|
|
(vector->generator vec start end)
|
|
|
|
Returns a generator that produces elements of ``vec``, in turn, from the index
|
|
``start`` (inclusive, default 0) to ``end`` (exclusive, default
|
|
``(vector-length vec)``). Mutating ``vec`` will affect the results of the
|
|
generator.
|
|
|
|
When given no arguments, ``vector->generator`` and ``generator->vector`` are
|
|
inverses up to ``equal?``; thus, for any vector ``x``, ``(equal? x
|
|
(generator->vector (vector->generator x))) => #t``.
|
|
|
|
# reverse-vector->generator
|
|
|
|
(reverse-vector->generator vec)
|
|
(reverse-vector->generator vec start)
|
|
(reverse-vector->generator vec start end)
|
|
|
|
Returns a generator that produces elements of ``vec``, in turn, from ``end``
|
|
(exclusive, default ``(vector-length vec)``) to ``start`` (inclusive, default
|
|
0), in reverse order of indices. Mutating ``vec`` will affect the results of the
|
|
generator.
|
|
|
|
# string->generator
|
|
|
|
(string->generator str)
|
|
(string->generator str start)
|
|
(string->generator str start end)
|
|
|
|
Returns a generator that produces characters of ``str``, in turn, from ``start``
|
|
(inclusive, default 0) to ``end`` (exclusive, default ``(string-length str)``).
|
|
Mutating ``str`` will affect the results of the generator.
|
|
|
|
When given no arguments, ``string->generator`` and ``generator->string`` are
|
|
inverses up to ``string=?``; thus, for any string ``s``, ``(string=? s
|
|
(generator->string (string->generator s))) => #t``.
|
|
|
|
# bytevector->generator
|
|
|
|
(bytevector->generator bv)
|
|
(bytevector->generator bv start)
|
|
(bytevector->generator bv start end)
|
|
|
|
Returns a generator that produces bytes of ``bv``, in turn, from ``start``
|
|
(inclusive, default 0) to ``end`` (exclusive, default ``(bytevector-length
|
|
bv)``). Mutating ``bv`` will affect the results of the generator.
|
|
|
|
# make-for-each-generator
|
|
|
|
(make-for-each-generator for-each obj)
|
|
|
|
Constructs a generator over any collection ``obj``, using a ``for-each``
|
|
procedure appropriate to ``obj``. This must be a procedure that, when called as
|
|
``(for-each proc obj)`` calls ``proc`` on each element of ``obj``. Examples of
|
|
such procedures are ``for-each``, ``string-for-each`` and ``vector-for-each``.
|
|
The value returned by ``for-each`` is ignored. The generator is finite if the
|
|
collection is finite.
|
|
|
|
``obj`` need not be a conventional one (such as a string, list, etc), as long as
|
|
``for-each`` can invoke a procedure on everything that counts as a member.
|
|
|
|
# make-unfold-generator
|
|
|
|
(make-unfold-generator stop? mapper successor seed)
|
|
|
|
A generator similar to [SRFI 1][2]'s ``unfold``.
|
|
|
|
The ``stop?`` predicate takes a seed value and determines whether to stop. The
|
|
``mapper`` procedure calculates a value to be returned by the generator from a
|
|
seed value. The ``successor`` procedure calculates the next seed value from
|
|
the current seed value.
|
|
|
|
For each call of the resulting generator, ``stop?`` is called with the current
|
|
seed value. If it returns true, then the generator returns an end-of-file
|
|
object. Otherwise, it applies ``mapper`` to the current seed value to get the
|
|
value to return, and uses ``successor`` to update the seed value.
|
|
|
|
The generator is finite unless ``stop?`` is a constant function returning
|
|
``#f``.
|
|
|
|
# gcons\*
|
|
|
|
(gcons\* item ... gen)
|
|
|
|
Returns a generator that adds ``item ...`` in front of ``gen``. Once each of
|
|
``item ...`` has been produced, the generator is guaranteed to tail-call
|
|
``gen``.
|
|
|
|
# gappend
|
|
|
|
(gappend gen ...)
|
|
|
|
Returns a generator that yields the items from the first argument generator, and
|
|
once it is exhausted, the second generator, and so forth.
|
|
|
|
If any of the argument generators are infinite, subsequent argument generators
|
|
will never be asked to produce any values.
|
|
|
|
# gcombine
|
|
|
|
(gcombine proc seed gen gen2 ...)
|
|
|
|
Returns a generator for mapping with state. It produces a sequence of sub-folds
|
|
over ``proc``.
|
|
|
|
The ``proc`` argument is a procedure that takes as many arguments as there are
|
|
argument generators, plus one. It is called as ``(proc v1 v2 ... seed)``, where
|
|
``v1 v2 ...`` are the values produced by the argument generators, and ``seed``
|
|
is the current seed value. It must return two values: the produced value and the
|
|
next seed. The generator is exhausted when any of its argument generators are
|
|
exhausted, at which time, the remaining argument generators are in an undefined
|
|
state.
|
|
|
|
# gfilter
|
|
|
|
(gfilter pred gen)
|
|
|
|
Returns a generator that produces items from ``gen``, except those for which
|
|
``pred`` would return ``#f``.
|
|
|
|
# gremove
|
|
|
|
(gremove pred gen)
|
|
|
|
Equivalent to ``(gfilter (lambda (x) (not (pred x))) gen)``.
|
|
|
|
# gtake
|
|
|
|
(gtake gen k)
|
|
(gtake gen k padding)
|
|
|
|
A generator analogue to [SRFI 1][2]'s ``take``. Returns a generator that
|
|
produces (at most) the first ``k`` items of ``gen``. If ``gen`` is exhausted
|
|
before it can produce ``k`` items, the rest will be made up by producing the
|
|
``padding`` value.
|
|
|
|
# gdrop
|
|
|
|
(gdrop gen k)
|
|
|
|
A generator analogue to [SRFI 1][2]'s ``drop``. Returns a generator that
|
|
skips the first (at most) ``k`` items of ``gen``, then produces the rest. If
|
|
``k`` is greater than, or equal to, the total number of items ``gen`` could
|
|
produce, an empty generator is produced instead.
|
|
|
|
# gtake-while
|
|
|
|
(gtake-while pred gen)
|
|
|
|
A generator analogue to [SRFI 1][2]'s ``take-while``. Returns a generator that
|
|
produces values from ``gen`` until ``pred`` returns ``#f`` on a value.
|
|
|
|
# gdrop-while
|
|
|
|
(gdrop-while pred gen)
|
|
|
|
A generator analogue to [SRFI 1][2]'s ``drop-while``. Returns a generator that
|
|
discards values from ``gen`` until ``pred`` returns ``#t`` for a value, and then
|
|
produces values from ``gen``.
|
|
|
|
# gdelete
|
|
|
|
(gdelete item gen)
|
|
(gdelete item gen =)
|
|
|
|
Returns a generator which produces the same items as ``gen``, except any items
|
|
that are the same as ``item`` up to ``=``, which defaults to ``equal?``. ``=``
|
|
is passed exactly two arguments, one of which is the value produced by ``gen``.
|
|
|
|
# gdelete-neighbor-dups
|
|
|
|
(gdelete-neighbor-dups gen)
|
|
(gdelete-neighbor-dups gen =)
|
|
|
|
Returns a generator which produces the same items as ``gen``, except any items
|
|
that are the same as the preceding items up to ``=``, which defaults to
|
|
``equal?``. ``=`` is passed exactly two arguments; the first of which is
|
|
produced by ``gen`` before the second.
|
|
|
|
# gindex
|
|
|
|
(gindex value-gen index-gen)
|
|
|
|
Returns a generator that produces elements of ``value-gen`` specified by the
|
|
indices (non-negative exact integers) produced by ``index-gen``. It is an error
|
|
if the indices are not strictly increasing, or if any index exceeds the number
|
|
of elements produced by ``value-gen``. The generator is exhausted when either of
|
|
``value-gen`` or ``index-gen`` is exhausted, at which time the other is in an
|
|
undefined state.
|
|
|
|
# gselect
|
|
|
|
(gselect value-gen truth-gen)
|
|
|
|
Returns a generator that produces elements of ``value-gen`` that correspond to
|
|
the values produced by ``truth-gen``. If the current value of ``truth-gen`` is
|
|
true, the current value of ``value-gen`` is produced; otherwise, the value is
|
|
skipped. The generator is exhausted when either of ``value-gen`` or
|
|
``truth-gen`` is exhausted, at which time the other is in an undefined state.
|
|
|
|
# generator->list
|
|
|
|
(generator->list gen)
|
|
(generator->list gen k)
|
|
|
|
Calls ``gen`` repeatedly to produce its values, then collects them into a list
|
|
and returns them. If ``k`` is omitted, ``gen`` will be asked to produce values
|
|
until it is exhausted; otherwise, only at most ``k``-many values will be
|
|
requested. It is an error for ``k`` to be anything other than a non-negative
|
|
integer.
|
|
|
|
# generator->reverse-list
|
|
|
|
(generator->reverse-list gen)
|
|
(generator->reverse-list gen k)
|
|
|
|
As ``generator->list``, but the returned list is in reverse order.
|
|
|
|
# generator->vector
|
|
|
|
(generator->vector gen)
|
|
(generator->vector gen k)
|
|
|
|
As ``generator->list``, but the returned result is a vector.
|
|
|
|
# generator->vector!
|
|
|
|
(generator->vector! vec at gen)
|
|
|
|
Calls ``gen`` repeatedly to produce its values, and puts them into ``vec``,
|
|
starting at index ``at``, until ``vec`` is full or ``gen`` is exhausted. Returns
|
|
the number of elements produced from ``gen``.
|
|
|
|
# generator->string
|
|
|
|
(generator->string gen)
|
|
(generator->string gen k)
|
|
|
|
Calls ``gen`` repeatedly to produce characters, and returns a newly-allocated
|
|
string of them. It is an error if ``gen`` does not produce only characters. If
|
|
``k`` is omitted, the generator will be asked to produce characters until it is
|
|
exhausted; otherwise, at most ``k`` characters will be requested. It is an error
|
|
for ``k`` to be anything other than a non-negative integer.
|
|
|
|
# generator-fold
|
|
|
|
(generator-fold proc seed gen1 gen2 ...)
|
|
|
|
An analogue of [SRFI 1][2]'s ``fold`` on values produced by the generator
|
|
arguments.
|
|
|
|
When one generator argument ``gen`` is given, for each value ``v`` produced by
|
|
``gen``, ``proc`` is called as ``(proc v r)``, where ``r`` is the current
|
|
accumulated result; the initial value of ``r`` is ``seed``, and the return value
|
|
from ``proc`` becomes the new accumulated result. When ``gen`` is exhausted, the
|
|
accumulated result at the time is returned.
|
|
|
|
When more than one generator argument is given, ``proc`` is called on all the
|
|
values produced by all the generator arguments, followed by the current
|
|
accumulated result. The procedure returns when any of the generator arguments is
|
|
exhausted, at which time the others are in an undefined state.
|
|
|
|
# generator-for-each
|
|
|
|
(generator-for-each proc gen1 gen2 ...)
|
|
|
|
A generator analogue of ``for-each`` that consumes produced values with side
|
|
effects. ``proc`` is repeatedly applied to values produced by all the generator
|
|
arguments, until any of them is exhausted. The values returned by ``proc`` are
|
|
discarded. The procedure terminates when any of the argument generators is
|
|
exhausted, at which time the others are in an undefined state.
|
|
|
|
# generator-find
|
|
|
|
(generator-find pred gen)
|
|
|
|
Returns the first value produced by ``gen`` that satisfies the predicate
|
|
``pred``, or ``#f`` if no such value exists. If ``gen`` is infinite, this
|
|
procedure will not return if it cannot find an appropriate item.
|
|
|
|
# generator-count
|
|
|
|
(generator-count pred gen)
|
|
|
|
Returns the number of values that gen can produce which satisfy the predicate
|
|
``pred``. This procedure will not return if ``gen`` is infinite.
|
|
|
|
# generator-any
|
|
|
|
(generator-any pred gen)
|
|
|
|
Applies ``pred`` to each item produced by ``gen``. As soon as ``pred`` returns a
|
|
true value, the value is returned without consuming the rest of ``gen``. If
|
|
``gen`` is exhausted, returns ``#f``.
|
|
|
|
# generator-every
|
|
|
|
(generator-every pred gen)
|
|
|
|
Equivalent to ``(not (generator-any (lambda (x) (not (pred x))) gen))``.
|
|
|
|
# generator-unfold
|
|
|
|
(generator-unfold gen unfold arg ...)
|
|
|
|
Equivalent to ``(unfold eof-object? (lambda (x) x) (lambda (x) (gen)) arg
|
|
...)``, where ``unfold`` is the [SRFI 1][http://srfi.schemers.org/srfi-1/srfi-1.html] procedure of the same name.
|