983 lines
39 KiB
Racket
983 lines
39 KiB
Racket
#lang scribble/doc
|
|
@(require "mz.rkt" scribble/scheme
|
|
(for-syntax racket/base)
|
|
(for-label racket/generator
|
|
racket/generic
|
|
racket/mpair))
|
|
|
|
@(define (info-on-seq where what)
|
|
@margin-note{See @secref[where] for information on using @|what| as
|
|
sequences.})
|
|
|
|
@title[#:style 'toc #:tag "sequences+streams"]{Sequences and Streams}
|
|
|
|
@tech{Sequences} and @tech{streams} abstract over iteration of elements in a
|
|
collection. Sequences allow iteration with @racket[for] macros or with sequence
|
|
operations such as @racket[sequence-map]. Streams are functional sequences that
|
|
can be used either in a generic way or a stream-specific way. @tech{Generators}
|
|
are closely related stateful objects that can be converted to a sequence and
|
|
vice-versa.
|
|
|
|
@local-table-of-contents[]
|
|
|
|
@; ======================================================================
|
|
@section[#:tag "sequences"]{Sequences}
|
|
|
|
@(define stream-evaluator
|
|
(let ([evaluator (make-base-eval)])
|
|
(evaluator '(require racket/generic))
|
|
(evaluator '(require racket/list))
|
|
(evaluator '(require racket/stream))
|
|
evaluator))
|
|
|
|
@guideintro["sequences"]{sequences}
|
|
|
|
A @deftech{sequence} encapsulates an ordered collection of values.
|
|
The elements of a sequence can be extracted with one of the
|
|
@racket[for] syntactic forms, with the procedures returned by
|
|
@racket[sequence-generate], or by converting the sequence into a
|
|
@tech{stream}.
|
|
|
|
The sequence datatype overlaps with many other datatypes. Among
|
|
built-in datatypes, the sequence datatype includes the following:
|
|
|
|
@itemize[
|
|
|
|
@item{exact nonnegative integers (see below)}
|
|
|
|
@item{strings (see @secref["strings"])}
|
|
|
|
@item{byte strings (see @secref["bytestrings"])}
|
|
|
|
@item{lists (see @secref["pairs"])}
|
|
|
|
@item{mutable lists (see @secref["mpairs"])}
|
|
|
|
@item{vectors (see @secref["vectors"])}
|
|
|
|
@item{hash tables (see @secref["hashtables"])}
|
|
|
|
@item{dictionaries (see @secref["dicts"])}
|
|
|
|
@item{sets (see @secref["sets"])}
|
|
|
|
@item{input ports (see @secref["ports"])}
|
|
|
|
@item{streams (see @secref["streams"])}
|
|
|
|
]
|
|
|
|
An @tech{exact number} @racket[_k] that is a non-negative
|
|
@tech{integer} acts as a sequence similar to @racket[(in-range _k)],
|
|
except that @racket[_k] by itself is not a @tech{stream}.
|
|
|
|
Custom sequences can be defined using structure type properties.
|
|
The easiest method to define a custom sequence is to use the
|
|
@racket[gen:stream] @tech{generic interface}. Streams are a suitable
|
|
abstraction for data structures that are directly iterable.
|
|
For example, a list is directly iterable with @racket[first] and
|
|
@racket[rest]. On the other hand, vectors are not directly
|
|
iterable: iteration has to go through an index. For data
|
|
structures that are not directly iterable, the @deftech{iterator}
|
|
for the data structure can be defined to be a stream
|
|
(e.g., a structure containing the index of a vector).
|
|
|
|
For example, unrolled linked lists (represented as a list of vectors)
|
|
themeselves do not fit the stream abstraction, but have index-based iterators
|
|
that can be represented as streams:
|
|
|
|
@examples[#:eval stream-evaluator
|
|
(struct unrolled-list-iterator (idx lst)
|
|
#:methods gen:stream
|
|
[(define (stream-empty? iter)
|
|
(define lst (unrolled-list-iterator-lst iter))
|
|
(or (null? lst)
|
|
(and (>= (unrolled-list-iterator-idx iter)
|
|
(vector-length (first lst)))
|
|
(null? (rest lst)))))
|
|
(define (stream-first iter)
|
|
(vector-ref (first (unrolled-list-iterator-lst iter))
|
|
(unrolled-list-iterator-idx iter)))
|
|
(define (stream-rest iter)
|
|
(define idx (unrolled-list-iterator-idx iter))
|
|
(define lst (unrolled-list-iterator-lst iter))
|
|
(if (>= idx (sub1 (vector-length (first lst))))
|
|
(unrolled-list-iterator 0 (rest lst))
|
|
(unrolled-list-iterator (add1 idx) lst)))])
|
|
|
|
(define (make-unrolled-list-iterator ul)
|
|
(unrolled-list-iterator 0 (unrolled-list-lov ul)))
|
|
|
|
(struct unrolled-list (lov)
|
|
#:property prop:sequence
|
|
make-unrolled-list-iterator)
|
|
|
|
(define ul1 (unrolled-list '(#(cracker biscuit) #(cookie scone))))
|
|
(for/list ([x ul1]) x)
|
|
]
|
|
|
|
The @racket[prop:sequence] property provides more flexibility in
|
|
specifying iteration, such as when a pre-processing step is needed
|
|
to prepare the data for iteration.
|
|
The @racket[make-do-sequence] function creates a sequence given a
|
|
thunk that returns procedures to implement a sequence, and the
|
|
@racket[prop:sequence] property can be associated with a structure
|
|
type to implement its implicit conversion to a sequence.
|
|
|
|
For most sequence types, extracting elements from a sequence has no
|
|
side-effect on the original sequence value; for example, extracting
|
|
the sequence of elements from a list does not change the list. For
|
|
other sequence types, each extraction implies a side effect; for
|
|
example, extracting the sequence of bytes from a port causes the bytes
|
|
to be read from the port. A sequence's state may either span all uses
|
|
of the sequence, as for a port, or it may be confined to each distinct
|
|
time that a sequence is @deftech{initiate}d by a @racket[for] form,
|
|
@racket[sequence->stream], @racket[sequence-generate], or
|
|
@racket[sequence-generate*]. Concretely, the thunk passed to
|
|
@racket[make-do-sequence] is called to @tech{initiate} the sequence
|
|
each time the sequence is used.
|
|
|
|
Individual elements of a sequence typically correspond to single values,
|
|
but an element may also correspond to multiple values. For example, a
|
|
hash table generates two values---a key and its value---for each element
|
|
in the sequence.
|
|
|
|
@; ----------------------------------------------------------------------
|
|
@subsection{Sequence Predicate and Constructors}
|
|
|
|
@defproc[(sequence? [v any/c]) boolean?]{
|
|
Returns @racket[#t] if @racket[v] can be used as a @tech{sequence},
|
|
@racket[#f] otherwise.}
|
|
|
|
@defproc*[([(in-range [end number?]) stream?]
|
|
[(in-range [start number?] [end number?] [step number? 1]) stream?])]{
|
|
Returns a sequence (that is also a @tech{stream}) whose elements are numbers. The single-argument
|
|
case @racket[(in-range end)] is equivalent to @racket[(in-range 0 end
|
|
1)]. The first number in the sequence is @racket[start], and each
|
|
successive element is generated by adding @racket[step] to the
|
|
previous element. The sequence stops before an element that would be
|
|
greater or equal to @racket[end] if @racket[step] is non-negative, or
|
|
less or equal to @racket[end] if @racket[step] is negative.
|
|
@speed[in-range "number"]}
|
|
|
|
@defproc[(in-naturals [start exact-nonnegative-integer? 0]) stream?]{
|
|
Returns an infinite sequence (that is also a @tech{stream}) of exact integers starting with
|
|
@racket[start], where each element is one more than the preceding
|
|
element. @speed[in-naturals "integer"]}
|
|
|
|
@defproc[(in-list [lst list?]) stream?]{
|
|
Returns a sequence (that is also a @tech{stream}) that is equivalent
|
|
to using @racket[lst] directly as a sequence.
|
|
@info-on-seq["pairs" "lists"]
|
|
@speed[in-list "list"]}
|
|
|
|
@defproc[(in-mlist [mlst mlist?]) sequence?]{
|
|
Returns a sequence equivalent to @racket[mlst].
|
|
@info-on-seq["mpairs" "mutable lists"]
|
|
@speed[in-mlist "mutable list"]}
|
|
|
|
@defproc[(in-vector [vec vector?]
|
|
[start exact-nonnegative-integer? 0]
|
|
[stop (or/c exact-integer? #f) #f]
|
|
[step (and/c exact-integer? (not/c zero?)) 1])
|
|
sequence?]{
|
|
Returns a sequence equivalent to @racket[vec] when no optional
|
|
arguments are supplied.
|
|
|
|
@info-on-seq["vectors" "vectors"]
|
|
|
|
The optional arguments @racket[start], @racket[stop], and
|
|
@racket[step] are analogous to @racket[in-range], except that a
|
|
@racket[#f] value for @racket[stop] is equivalent to
|
|
@racket[(vector-length vec)]. That is, the first element in the
|
|
sequence is @racket[(vector-ref vec start)], and each successive
|
|
element is generated by adding @racket[step] to index of the previous
|
|
element. The sequence stops before an index that would be greater or
|
|
equal to @racket[end] if @racket[step] is non-negative, or less or
|
|
equal to @racket[end] if @racket[step] is negative.
|
|
|
|
If @racket[start] is not a valid index, or @racket[stop]
|
|
is not in [-1, @racket[(vector-length vec)]] then the @exnraise[exn:fail:contract].
|
|
If @racket[start] is less than @racket[stop] and @racket[step] is
|
|
negative, then the @exnraise[exn:fail:contract:mismatch]. Similarly,
|
|
if @racket[start] is more than @racket[stop] and @racket[step] is
|
|
positive, then the @exnraise[exn:fail:contract:mismatch].
|
|
|
|
@speed[in-vector "vector"]}
|
|
|
|
@defproc[(in-string [str string?]
|
|
[start exact-nonnegative-integer? 0]
|
|
[stop (or/c exact-integer? #f) #f]
|
|
[step (and/c exact-integer? (not/c zero?)) 1])
|
|
sequence?]{
|
|
Returns a sequence equivalent to @racket[str] when no optional
|
|
arguments are supplied.
|
|
|
|
@info-on-seq["strings" "strings"]
|
|
|
|
The optional arguments @racket[start], @racket[stop], and
|
|
@racket[step] are as in @racket[in-vector].
|
|
|
|
@speed[in-string "string"]}
|
|
|
|
@defproc[(in-bytes [bstr bytes?]
|
|
[start exact-nonnegative-integer? 0]
|
|
[stop (or/c exact-integer? #f) #f]
|
|
[step (and/c exact-integer? (not/c zero?)) 1])
|
|
sequence?]{
|
|
Returns a sequence equivalent to @racket[bstr] when no optional
|
|
arguments are supplied.
|
|
|
|
@info-on-seq["bytestrings" "byte strings"]
|
|
|
|
The optional arguments @racket[start], @racket[stop], and
|
|
@racket[step] are as in @racket[in-vector].
|
|
|
|
@speed[in-bytes "byte string"]}
|
|
|
|
@defproc[(in-port [r (input-port? . -> . any/c) read]
|
|
[in input-port? (current-input-port)])
|
|
sequence?]{
|
|
Returns a sequence whose elements are produced by calling @racket[r]
|
|
on @racket[in] until it produces @racket[eof].}
|
|
|
|
@defproc[(in-input-port-bytes [in input-port?]) sequence?]{
|
|
Returns a sequence equivalent to @racket[(in-port read-byte in)].}
|
|
|
|
@defproc[(in-input-port-chars [in input-port?]) sequence?]{
|
|
Returns a sequence whose elements are read as characters from
|
|
@racket[in] (equivalent to @racket[(in-port read-char in)]).}
|
|
|
|
@defproc[(in-lines [in input-port? (current-input-port)]
|
|
[mode (or/c 'linefeed 'return 'return-linefeed 'any 'any-one) 'any])
|
|
sequence?]{
|
|
Returns a sequence equivalent to
|
|
@racket[(in-port (lambda (p) (read-line p mode)) in)]. Note that the
|
|
default mode is @racket['any], whereas the default mode of
|
|
@racket[read-line] is @racket['linefeed].}
|
|
|
|
@defproc[(in-bytes-lines [in input-port? (current-input-port)]
|
|
[mode (or/c 'linefeed 'return 'return-linefeed 'any 'any-one) 'any])
|
|
sequence?]{
|
|
Returns a sequence equivalent to
|
|
@racket[(in-port (lambda (p) (read-bytes-line p mode)) in)]. Note
|
|
that the default mode is @racket['any], whereas the default mode of
|
|
@racket[read-bytes-line] is @racket['linefeed].}
|
|
|
|
@defproc[(in-hash [hash hash?]) sequence?]{
|
|
Returns a sequence equivalent to @racket[hash].
|
|
|
|
@examples[
|
|
(define table (hash 'a 1 'b 2))
|
|
(for ([(key value) (in-hash table)])
|
|
(printf "key: ~a value: ~a\n" key value))
|
|
]
|
|
|
|
@info-on-seq["hashtables" "hash tables"]}
|
|
|
|
@defproc[(in-hash-keys [hash hash?]) sequence?]{
|
|
Returns a sequence whose elements are the keys of @racket[hash].
|
|
|
|
@examples[
|
|
(define table (hash 'a 1 'b 2))
|
|
(for ([key (in-hash-keys table)])
|
|
(printf "key: ~a\n" key))
|
|
]
|
|
|
|
}
|
|
|
|
@defproc[(in-hash-values [hash hash?]) sequence?]{
|
|
Returns a sequence whose elements are the values of @racket[hash].
|
|
|
|
@examples[
|
|
(define table (hash 'a 1 'b 2))
|
|
(for ([value (in-hash-values table)])
|
|
(printf "value: ~a\n" value))
|
|
]
|
|
|
|
}
|
|
|
|
@defproc[(in-hash-pairs [hash hash?]) sequence?]{
|
|
Returns a sequence whose elements are pairs, each containing a key and
|
|
its value from @racket[hash] (as opposed to using @racket[hash]
|
|
directly as a sequence to get the key and value as separate values for
|
|
each element).
|
|
|
|
@examples[
|
|
(define table (hash 'a 1 'b 2))
|
|
(for ([key+value (in-hash-pairs table)])
|
|
(printf "key and value: ~a\n" key+value))
|
|
]
|
|
|
|
}
|
|
|
|
@defproc[(in-directory [dir (or/c #f path-string?) #f]) sequence?]{
|
|
Returns a sequence that produces all of the paths for files,
|
|
directories, and links within @racket[dir]. If @racket[dir] is not
|
|
@racket[#f], then every produced path starts with @racket[dir] as its
|
|
prefix. If @racket[dir] is @racket[#f], then paths in and relative to
|
|
the current directory are produced.
|
|
|
|
An @racket[in-directory] sequence traverses nested subdirectories
|
|
recursively. To generate a sequence that includes only the immediate
|
|
content of a directory, use the result of @racket[directory-list] as
|
|
a sequence.}
|
|
|
|
@defproc[(in-producer [producer procedure?] [stop any/c] [args any/c] ...)
|
|
sequence?]{
|
|
Returns a sequence that contains values from sequential calls to
|
|
@racket[producer]. A @racket[stop] value returned by
|
|
@racket[producer] marks the end of the sequence (and the
|
|
@racket[stop] value is not included in the sequence); @racket[stop]
|
|
can be a predicate that is applied to the results of @racket[producer],
|
|
or it can be a value that is tested against the result of
|
|
with @racket[eq?]. (The @racket[stop] argument must be a predicate
|
|
if the stop value is itself a function or if
|
|
@racket[producer] returns multiple values.)}
|
|
|
|
@defproc[(in-value [v any/c]) sequence?]{
|
|
Returns a sequence that produces a single value: @racket[v]. This
|
|
form is mostly useful for @racket[let]-like bindings in forms such as
|
|
@racket[for*/list].}
|
|
|
|
@defproc[(in-indexed [seq sequence?]) sequence?]{
|
|
Returns a sequence where each element has two values: the value
|
|
produced by @racket[seq], and a non-negative exact integer starting
|
|
with @racket[0]. The elements of @racket[seq] must be single-valued.}
|
|
|
|
@defproc[(in-sequences [seq sequence?] ...) sequence?]{
|
|
Returns a sequence that is made of all input sequences, one after
|
|
the other. Each @racket[seq] is @tech{initiate}d only after the
|
|
preceding @racket[seq] is exhausted. If a single @racket[seq] is
|
|
provided, then @racket[seq] is returned; otherwise, the elements of
|
|
each @racket[seq] must all have the same number of values.}
|
|
|
|
@defproc[(in-cycle [seq sequence?] ...) sequence?]{
|
|
Similar to @racket[in-sequences], but the sequences are repeated in
|
|
an infinite cycle, where each @racket[seq] is @tech{initiate}d
|
|
afresh in each iteration. Beware that if no @racket[seq]s are
|
|
provided or if all @racket[seq]s become empty, then the sequence
|
|
produced by @racket[in-cycle] never returns when an element is
|
|
demanded---or even when the sequence is @tech{initiate}d, if all
|
|
@racket[seq]s are initially empty.}
|
|
|
|
@defproc[(in-parallel [seq sequence?] ...) sequence?]{
|
|
Returns a sequence where each element has as many values as the number
|
|
of supplied @racket[seq]s; the values, in order, are the values of
|
|
each @racket[seq]. The elements of each @racket[seq] must be
|
|
single-valued.}
|
|
|
|
@defproc[(in-values-sequence [seq sequence?]) sequence?]{
|
|
Returns a sequence that is like @racket[seq], but it combines
|
|
multiple values for each element from @racket[seq] as a list of
|
|
elements.}
|
|
|
|
@defproc[(in-values*-sequence [seq sequence?]) sequence?]{
|
|
Returns a sequence that is like @racket[seq], but when an element of
|
|
@racket[seq] has multiple values or a single list value, then the
|
|
values are combined in a list. In other words,
|
|
@racket[in-values*-sequence] is like @racket[in-values-sequence],
|
|
except that non-list, single-valued elements are not wrapped in a
|
|
list.}
|
|
|
|
@defproc[(stop-before [seq sequence?] [pred (any/c . -> . any)])
|
|
sequence?]{
|
|
Returns a sequence that contains the elements of @racket[seq] (which
|
|
must be single-valued), but only until the last element for which
|
|
applying @racket[pred] to the element produces @racket[#t], after
|
|
which the sequence ends.}
|
|
|
|
@defproc[(stop-after [seq sequence?] [pred (any/c . -> . any)])
|
|
sequence?]{
|
|
Returns a sequence that contains the elements of @racket[seq] (which
|
|
must be single-valued), but only until the element (inclusive) for
|
|
which applying @racket[pred] to the element produces @racket[#t],
|
|
after which the sequence ends.}
|
|
|
|
@defproc[(make-do-sequence [thunk (-> (values (any/c . -> . any)
|
|
(any/c . -> . any/c)
|
|
any/c
|
|
(or/c (any/c . -> . any/c) #f)
|
|
(or/c (() () #:rest list? . ->* . any/c) #f)
|
|
(or/c ((any/c) () #:rest list? . ->* . any/c) #f)))])
|
|
sequence?]{
|
|
Returns a sequence whose elements are generated by the procedures and
|
|
initial value returned by the thunk, which is called to @tech{initiate}
|
|
the sequence. The initiated sequence is defined in terms
|
|
of a @defterm{position}, which is initialized to the third result of
|
|
the thunk, and the @defterm{element}, which may consist of multiple
|
|
values.
|
|
|
|
The @racket[thunk] results define the generated elements as follows:
|
|
@itemize[
|
|
@item{The first result is a @racket[_pos->element] procedure that takes
|
|
the current position and returns the value(s) for the current
|
|
element.}
|
|
@item{The second result is a @racket[_next-pos] procedure that takes
|
|
the current position and returns the next position.}
|
|
@item{The third result is the initial position.}
|
|
@item{The fourth result is a @racket[_continue-with-pos?] function that
|
|
takes the current position and returns a
|
|
true result if the sequence includes the value(s) for the current
|
|
position, and false if the sequence should end instead of
|
|
including the value(s). Alternatively, the fourth result can be
|
|
@racket[#f] to indicate that the sequence should always include the
|
|
current value(s).}
|
|
@item{The fifth result is a @racket[_continue-with-val?] function that is
|
|
like the fourth result, but it takes the
|
|
current element value(s) instead of the current position.
|
|
Alternatively, the fifth result can be
|
|
@racket[#f] to indicate that the sequence should always include the
|
|
value(s) at the current position.}
|
|
@item{The sixth result is a @racket[_continue-after-pos+val?] procedure
|
|
that takes both
|
|
the current position and the current element value(s) and
|
|
determines whether the sequence ends after the current element is already
|
|
included in the sequence.
|
|
Alternatively, the sixth result can be
|
|
@racket[#f] to indicate that the sequence can always continue
|
|
after the current value(s).}]
|
|
|
|
Each of the procedures listed above is called only once per position.
|
|
Among the last three procedures, as soon as one of the procedures
|
|
returns @racket[#f], the sequence ends, and none are called again.
|
|
Typically, one of the functions determines the end condition, and
|
|
@racket[#f] is used in place of the other two functions.}
|
|
|
|
@defthing[prop:sequence struct-type-property?]{
|
|
|
|
Associates a procedure to a structure type that takes an instance of
|
|
the structure and returns a sequence. If @racket[v] is an instance of
|
|
a structure type with this property, then @racket[(sequence? v)]
|
|
produces @racket[#t].
|
|
|
|
Using a pre-existing sequence:
|
|
|
|
@examples[
|
|
(struct my-set (table)
|
|
#:property prop:sequence
|
|
(lambda (s)
|
|
(in-hash-keys (my-set-table s))))
|
|
(define (make-set . xs)
|
|
(my-set (for/hash ([x (in-list xs)])
|
|
(values x #t))))
|
|
(for/list ([c (make-set 'celeriac 'carrot 'potato)])
|
|
c)]
|
|
|
|
Using @racket[make-do-sequence]:
|
|
|
|
@let-syntax[([car (make-element-id-transformer
|
|
(lambda (id) #'@racketidfont{car}))])
|
|
@examples[
|
|
(define-struct train (car next)
|
|
#:property prop:sequence (lambda (t)
|
|
(make-do-sequence
|
|
(lambda ()
|
|
(values train-car
|
|
train-next
|
|
t
|
|
(lambda (t) t)
|
|
(lambda (v) #t)
|
|
(lambda (t v) #t))))))
|
|
(for/list ([c (make-train 'engine
|
|
(make-train 'boxcar
|
|
(make-train 'caboose
|
|
#f)))])
|
|
c)]]}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
@subsection{Sequence Conversion}
|
|
|
|
@defproc[(sequence->stream [seq sequence?]) stream?]{
|
|
Coverts a sequence to a @tech{stream}, which supports the
|
|
@racket[stream-first] and @racket[stream-rest] operations. Creation
|
|
of the stream eagerly @tech{initiates} the sequence, but the
|
|
stream lazily draws elements from the sequence, caching each element
|
|
so that @racket[stream-first] produces the same result each time
|
|
is applied to a stream.
|
|
|
|
In extracting an element from @racket[seq] involves a side-effect,
|
|
then the effect is performed each time that either
|
|
@racket[stream-first] or @racket[stream-rest] is first used to
|
|
access or skip an element.}
|
|
|
|
@defproc[(sequence-generate [seq sequence?])
|
|
(values (-> boolean?) (-> any))]{
|
|
@tech{Initiates} a sequence and returns two thunks to extract elements
|
|
from the sequence. The first
|
|
returns @racket[#t] if more values are available for the sequence.
|
|
The second returns the next element (which may be multiple values)
|
|
from the sequence; if no more elements are available, the
|
|
@exnraise[exn:fail:contract].}
|
|
|
|
@defproc[(sequence-generate* [seq sequence?])
|
|
(values (or/c list? #f)
|
|
(-> (values (or/c list? #f) procedure?)))]{
|
|
Like @racket[sequence-generate], but avoids state (aside from any
|
|
inherent in the sequence) by returning a list of values for the sequence's
|
|
first element---or @racket[#f] if the sequence is empty---and a thunk
|
|
to continue with the sequence; the result of the thunk is the same
|
|
as the result of @racket[sequence-generate*], but for the second
|
|
element of the sequence, and so on. If the thunk is called when the
|
|
element result is @racket[#f] (indicating no further values in the sequence),
|
|
the @exnraise[exn:fail:contract].}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
@subsection[#:tag "more-sequences"]{Sequence Combinations}
|
|
|
|
@note-lib[racket/sequence]
|
|
|
|
@defthing[empty-sequence sequence?]{
|
|
A sequence with no elements.}
|
|
|
|
@defproc[(sequence->list [s sequence?]) list?]{
|
|
Returns a list whose elements are the elements of @racket[s],
|
|
each of which must be a single value. If @racket[s] is infinite, this
|
|
function does not terminate.}
|
|
|
|
@defproc[(sequence-length [s sequence?])
|
|
exact-nonnegative-integer?]{
|
|
Returns the number of elements of @racket[s] by extracting and
|
|
discarding all of them. If @racket[s] is infinite, this function
|
|
does not terminate.}
|
|
|
|
@defproc[(sequence-ref [s sequence?] [i exact-nonnegative-integer?])
|
|
any]{
|
|
Returns the @racket[i]th element of @racket[s] (which may be
|
|
multiple values).}
|
|
|
|
@defproc[(sequence-tail [s sequence?] [i exact-nonnegative-integer?])
|
|
sequence?]{
|
|
Returns a sequence equivalent to @racket[s], except that the first
|
|
@racket[i] elements are omitted.
|
|
|
|
In case @tech[#:key "initiate"]{initiating} @racket[s] involves a
|
|
side effect, the sequence @racket[s] is not @tech{initiate}d
|
|
until the resulting sequence is @tech{initiate}d, at which point the
|
|
first @racket[i] elements are extracted from
|
|
the sequence.}
|
|
|
|
@defproc[(sequence-append [s sequence?] ...)
|
|
sequence?]{
|
|
Returns a sequence that contains all elements of each sequence in the
|
|
order they appear in the original sequences. The new sequence is
|
|
constructed lazily.
|
|
|
|
If all given @racket[s]s are @tech{streams}, the result is also a
|
|
@tech{stream}.}
|
|
|
|
@defproc[(sequence-map [f procedure?]
|
|
[s sequence?])
|
|
sequence?]{
|
|
Returns a sequence that contains @racket[f] applied to each element of
|
|
@racket[s]. The new sequence is constructed lazily.
|
|
|
|
If @racket[s] is a @tech{stream}, then the result is also a
|
|
@tech{stream}.}
|
|
|
|
@defproc[(sequence-andmap [f (-> any/c ... boolean?)]
|
|
[s sequence?])
|
|
boolean?]{
|
|
Returns @racket[#t] if @racket[f] returns a true result on every
|
|
element of @racket[s]. If @racket[s] is infinite and @racket[f] never
|
|
returns a false result, this function does not terminate.}
|
|
|
|
@defproc[(sequence-ormap [f (-> any/c ... boolean?)]
|
|
[s sequence?])
|
|
boolean?]{
|
|
Returns @racket[#t] if @racket[f] returns a true result on some
|
|
element of @racket[s]. If @racket[s] is infinite and @racket[f] never
|
|
returns a true result, this function does not terminate.}
|
|
|
|
@defproc[(sequence-for-each [f (-> any/c ... any)]
|
|
[s sequence?])
|
|
void?]{
|
|
Applies @racket[f] to each element of @racket[s]. If @racket[s] is
|
|
infinite, this function does not terminate.}
|
|
|
|
@defproc[(sequence-fold [f (-> any/c any/c ... any/c)]
|
|
[i any/c]
|
|
[s sequence?])
|
|
any/c]{
|
|
Folds @racket[f] over each element of @racket[s] with @racket[i] as
|
|
the initial accumulator. If @racket[s] is infinite, this function
|
|
does not terminate. The @racket[f] function takes the accumulator as its first argument
|
|
and the next sequence element as its second.}
|
|
|
|
@defproc[(sequence-count [f procedure?] [s sequence?])
|
|
exact-nonnegative-integer?]{
|
|
Returns the number of elements in @racket[s] for which @racket[f]
|
|
returns a true result. If @racket[s] is infinite, this function does
|
|
not terminate.}
|
|
|
|
@defproc[(sequence-filter [f (-> any/c ... boolean?)]
|
|
[s sequence?])
|
|
sequence?]{
|
|
Returns a sequence whose elements are the elements of @racket[s] for
|
|
which @racket[f] returns a true result. Although the new sequence is
|
|
constructed lazily, if @racket[s] has an infinite number of elements
|
|
where @racket[f] returns a false result in between two elements where
|
|
@racket[f] returns a true result, then operations on this sequence will
|
|
not terminate during the infinite sub-sequence.
|
|
|
|
If @racket[s] is a @tech{stream}, then the result is also a
|
|
@tech{stream}.}
|
|
|
|
@defproc[(sequence-add-between [s sequence?] [e any/c])
|
|
sequence?]{
|
|
Returns a sequence whose elements are the elements of @racket[s],
|
|
but with @racket[e] between each pair of elements in @racket[s].
|
|
The new sequence is constructed lazily.
|
|
|
|
If @racket[s] is a @tech{stream}, then the result is also a
|
|
@tech{stream}.}
|
|
|
|
@; ======================================================================
|
|
@section[#:tag "streams"]{Streams}
|
|
|
|
A @deftech{stream} is a kind of sequence that supports functional
|
|
iteration via @racket[stream-first] and @racket[stream-rest]. The
|
|
@racket[stream-cons] form constructs a lazy stream, but plain lists
|
|
can be used as stream, and functions such as @racket[in-range] and
|
|
@racket[in-naturals] also create streams.
|
|
|
|
@note-lib[racket/stream]
|
|
|
|
@defproc[(stream? [v any/c]) boolean?]{
|
|
Returns @racket[#t] if @racket[v] can be used as a @tech{stream},
|
|
@racket[#f] otherwise.}
|
|
|
|
@defproc[(stream-empty? [s stream?]) boolean?]{
|
|
Returns @racket[#f] if @racket[s] has no elements, @racket[#f]
|
|
otherwise.
|
|
}
|
|
|
|
@defproc[(stream-first [s (and/c stream? (not/c stream-empty?))]) any]{
|
|
Returns the value(s) of the first element in @racket[s].
|
|
}
|
|
|
|
@defproc[(stream-rest [s (and/c stream? (not/c stream-empty?))]) stream?]{
|
|
Returns a stream that is equivalent to @racket[s] without its
|
|
first element.
|
|
}
|
|
|
|
@defform[(stream-cons first-expr rest-expr)]{
|
|
|
|
Produces a lazy stream for which @racket[stream-first] forces the
|
|
evaluation of @racket[first-expr] to produce the first element of the
|
|
stream, and @racket[stream-rest] forces the evaluation of
|
|
@racket[rest-expr] to produce a stream for the rest of the returned
|
|
stream.
|
|
|
|
The first element of the stream as produced by @racket[first-expr]
|
|
must be a single value. The @racket[rest-expr] must produce a stream
|
|
when it is evaluated, otherwise the @exnraise[exn:fail:contract?].}
|
|
|
|
@defform[(stream expr ...)]{
|
|
|
|
A shorthand for nested @racket[stream-cons]es ending with
|
|
@racket[empty-stream].}
|
|
|
|
@defproc[(in-stream [s stream?]) sequence?]{
|
|
Returns a sequence that is equivalent to @racket[s].
|
|
@speed[in-stream "streams"]}
|
|
|
|
@defthing[empty-stream stream?]{
|
|
A stream with no elements.}
|
|
|
|
@defproc[(stream->list [s stream?]) list?]{
|
|
Returns a list whose elements are the elements of @racket[s],
|
|
each of which must be a single value. If @racket[s] is infinite, this
|
|
function does not terminate.}
|
|
|
|
@defproc[(stream-length [s stream?])
|
|
exact-nonnegative-integer?]{
|
|
Returns the number of elements of @racket[s]. If @racket[s] is
|
|
infinite, this function does not terminate.
|
|
|
|
In the case of lazy streams, this function forces evaluation only of
|
|
the sub-streams, and not the stream's elements.}
|
|
|
|
@defproc[(stream-ref [s stream?] [i exact-nonnegative-integer?])
|
|
any]{
|
|
Returns the @racket[i]th element of @racket[s] (which may be
|
|
multiple values).}
|
|
|
|
@defproc[(stream-tail [s stream?] [i exact-nonnegative-integer?])
|
|
stream?]{
|
|
Returns a stream equivalent to @racket[s], except that the first
|
|
@racket[i] elements are omitted.
|
|
|
|
In case extracting elements from @racket[s] involves a side effect,
|
|
they will not be extracted until the first element is extracted from
|
|
the resulting stream.}
|
|
|
|
@defproc[(stream-append [s stream?] ...)
|
|
stream?]{
|
|
Returns a stream that contains all elements of each stream in the
|
|
order they appear in the original streams. The new stream is
|
|
constructed lazily.}
|
|
|
|
@defproc[(stream-map [f procedure?]
|
|
[s stream?])
|
|
stream?]{
|
|
Returns a stream that contains @racket[f] applied to each element of
|
|
@racket[s]. The new stream is constructed lazily.}
|
|
|
|
@defproc[(stream-andmap [f (-> any/c ... boolean?)]
|
|
[s stream?])
|
|
boolean?]{
|
|
Returns @racket[#t] if @racket[f] returns a true result on every
|
|
element of @racket[s]. If @racket[s] is infinite and @racket[f] never
|
|
returns a false result, this function does not terminate.}
|
|
|
|
@defproc[(stream-ormap [f (-> any/c ... boolean?)]
|
|
[s stream?])
|
|
boolean?]{
|
|
Returns @racket[#t] if @racket[f] returns a true result on some
|
|
element of @racket[s]. If @racket[s] is infinite and @racket[f] never
|
|
returns a true result, this function does not terminate.}
|
|
|
|
@defproc[(stream-for-each [f (-> any/c ... any)]
|
|
[s stream?])
|
|
void?]{
|
|
Applies @racket[f] to each element of @racket[s]. If @racket[s] is
|
|
infinite, this function does not terminate.}
|
|
|
|
@defproc[(stream-fold [f (-> any/c any/c ... any/c)]
|
|
[i any/c]
|
|
[s stream?])
|
|
any/c]{
|
|
Folds @racket[f] over each element of @racket[s] with @racket[i] as
|
|
the initial accumulator. If @racket[s] is infinite, this function
|
|
does not terminate.}
|
|
|
|
@defproc[(stream-count [f procedure?] [s stream?])
|
|
exact-nonnegative-integer?]{
|
|
Returns the number of elements in @racket[s] for which @racket[f]
|
|
returns a true result. If @racket[s] is infinite, this function does
|
|
not terminate.}
|
|
|
|
@defproc[(stream-filter [f (-> any/c ... boolean?)]
|
|
[s stream?])
|
|
stream?]{
|
|
Returns a stream whose elements are the elements of @racket[s] for
|
|
which @racket[f] returns a true result. Although the new stream is
|
|
constructed lazily, if @racket[s] has an infinite number of elements
|
|
where @racket[f] returns a false result in between two elements where
|
|
@racket[f] returns a true result, then operations on this stream will
|
|
not terminate during the infinite sub-stream.}
|
|
|
|
@defproc[(stream-add-between [s stream?] [e any/c])
|
|
stream?]{
|
|
Returns a stream whose elements are the elements of @racket[s],
|
|
but with @racket[e] between each pair of elements in @racket[s].
|
|
The new stream is constructed lazily.}
|
|
|
|
@defthing[gen:stream any/c]{
|
|
|
|
Associates three methods to a structure type to implement the
|
|
@tech{generic interface} (see @secref["struct-generics"]) for
|
|
streams.
|
|
|
|
To supply method implementations, the @racket[#:methods] keyword
|
|
should be used in a structure type definition. The following three
|
|
methods should be implemented:
|
|
|
|
@itemize[
|
|
@item{@racket[stream-empty?] : accepts one argument}
|
|
@item{@racket[stream-first] : accepts one argument}
|
|
@item{@racket[stream-rest] : accepts one argument}
|
|
]
|
|
|
|
@examples[#:eval stream-evaluator
|
|
(define-struct list-stream (v)
|
|
#:methods gen:stream
|
|
[(define (stream-empty? stream)
|
|
(empty? (list-stream-v stream)))
|
|
(define (stream-first stream)
|
|
(first (list-stream-v stream)))
|
|
(define (stream-rest stream)
|
|
(rest (list-stream-v stream)))])
|
|
|
|
(define l1 (list-stream '(1 2)))
|
|
(stream? l1)
|
|
(stream-first l1)
|
|
]}
|
|
|
|
@defthing[prop:stream struct-type-property?]{
|
|
A deprecated structure type property used to define custom
|
|
extensions to the stream API. Use @racket[gen:stream] instead.
|
|
Accepts a vector of three procedures taking the same arguments
|
|
as the methods in @racket[gen:stream].
|
|
}
|
|
|
|
@; ======================================================================
|
|
@section{Generators}
|
|
|
|
A @deftech{generator} is a procedure that returns a sequence of
|
|
values, incrementing the sequence each time that the generator is
|
|
called. In particular, the @racket[generator] form implements a
|
|
generator by evaluating a body that calls @racket[yield] to return
|
|
values from the generator.
|
|
|
|
@defmodule[racket/generator]
|
|
|
|
@(define generator-eval
|
|
(let ([the-eval (make-base-eval)])
|
|
(the-eval '(require racket/generator))
|
|
the-eval))
|
|
|
|
|
|
@defproc[(generator? [v any/c]) boolean?]{
|
|
Return @racket[#t] if @racket[v] is a @tech{generator},
|
|
@racket[#f] otherwise.}
|
|
|
|
|
|
@defform[(generator formals body ...+)]{
|
|
Creates a @tech{generator}, where @racket[formals] is like the
|
|
@racket[formals] of @racket[case-lambda] (i.e., the
|
|
@racket[_kw-formals] of @racket[lambda] restricted to non-optional
|
|
and non-keyword arguments).
|
|
|
|
For the first call to a generator, the arguments are bound to the
|
|
@racket[formals] and evaluation of @racket[body] starts. During the
|
|
@tech{dynamic extent} of @racket[body], the generator can return
|
|
immediately using the @racket[yield] function. A second call to the
|
|
generator resumes at the @racket[yield] call, producing the
|
|
arguments of the second call as the results of the @racket[yield],
|
|
and so on. The eventual results of @racket[body] are supplied to an
|
|
implicit final @racket[yield]; after that final @racket[yield],
|
|
calling the generator again returns the same values, but all such
|
|
calls must provide 0 arguments to the generator.
|
|
|
|
@examples[#:eval generator-eval
|
|
(define g (generator ()
|
|
(let loop ([x '(a b c)])
|
|
(if (null? x)
|
|
0
|
|
(begin
|
|
(yield (car x))
|
|
(loop (cdr x)))))))
|
|
(g)
|
|
(g)
|
|
(g)
|
|
(g)
|
|
(g)]}
|
|
|
|
@defproc[(yield [v any/c] ...) any]{
|
|
Returns @racket[v]s from a generator, saving the point of execution
|
|
inside a generator (i.e., within the @tech{dynamic extent} of a
|
|
@racket[generator] body) to be resumed by the next call to the
|
|
generator. The results of @racket[yield] are the arguments
|
|
that are provided to the next call of the generator.
|
|
|
|
When not in the @tech{dynamic extent} of a @racket[generator],
|
|
@racket[infinite-generator], or @racket[in-generator] body,
|
|
@racket[yield] raises @racket[exn:fail] after evaluating its
|
|
@racket[expr]s.
|
|
|
|
@examples[#:eval generator-eval
|
|
(define my-generator (generator () (yield 1) (yield 2 3 4)))
|
|
(my-generator)
|
|
(my-generator)]
|
|
|
|
@examples[#:eval generator-eval
|
|
(define pass-values-generator
|
|
(generator ()
|
|
(let* ([from-user (yield 2)]
|
|
[from-user-again (yield (add1 from-user))])
|
|
(yield from-user-again))))
|
|
|
|
(pass-values-generator)
|
|
(pass-values-generator 5)
|
|
(pass-values-generator 12)]}
|
|
|
|
@defform[(infinite-generator body ...+)]{
|
|
Like @racket[generator], but repeats evaluation of the @racket[body]s when
|
|
the last @racket[body] completes without implicitly @racket[yield]ing.
|
|
|
|
@examples[#:eval generator-eval
|
|
(define welcome
|
|
(infinite-generator
|
|
(yield 'hello)
|
|
(yield 'goodbye)))
|
|
(welcome)
|
|
(welcome)
|
|
(welcome)
|
|
(welcome)]}
|
|
|
|
@defform[(in-generator body ...+)]{
|
|
Produces a @tech{sequence} that encapsulates the @tech{generator} formed by
|
|
@racket[(generator () body ...+)]. The values produced by the
|
|
generator form the elements of the sequence.
|
|
|
|
@examples[#:eval generator-eval
|
|
(for/list ([i (in-generator
|
|
(let loop ([x '(a b c)])
|
|
(when (not (null? x))
|
|
(yield (car x))
|
|
(loop (cdr x)))))])
|
|
i)]
|
|
|
|
To use an existing generator as a sequence, use @racket[in-producer]
|
|
with a stop-value known for the generator.
|
|
|
|
@examples[#:eval generator-eval
|
|
(define my-stop-value (gensym))
|
|
(define my-generator (generator ()
|
|
(let loop ([x '(a b 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)]}
|
|
|
|
@defproc[(generator-state [g generator?]) symbol?]{
|
|
Returns a symbol that describes the state of the generator.
|
|
|
|
@itemize[
|
|
@item{@racket['fresh] --- The generator has been freshly created and
|
|
has not been called yet.}
|
|
@item{@racket['suspended] --- Control within the generator has been
|
|
suspended due to a call to @racket[yield]. The generator can
|
|
be called.}
|
|
@item{@racket['running] --- The generator is currently executing.}
|
|
@item{@racket['done] --- The generator has executed its entire
|
|
body and will continue to produce the same result as from
|
|
the last call.}]
|
|
|
|
@examples[#:eval generator-eval
|
|
(define my-generator (generator () (yield 1) (yield 2)))
|
|
(generator-state my-generator)
|
|
(my-generator)
|
|
(generator-state my-generator)
|
|
(my-generator)
|
|
(generator-state my-generator)
|
|
(my-generator)
|
|
(generator-state my-generator)
|
|
|
|
(define introspective-generator (generator () ((yield 1))))
|
|
(introspective-generator)
|
|
(introspective-generator
|
|
(lambda () (generator-state introspective-generator)))
|
|
(generator-state introspective-generator)
|
|
(introspective-generator)]}
|
|
|
|
@defproc[(sequence->generator [s sequence?]) (-> any)]{
|
|
Converts a @tech{sequence} to a @tech{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-const|
|
|
as its final result.}
|
|
|
|
@defproc[(sequence->repeated-generator [s sequence?]) (-> any)]{
|
|
Like @racket[sequence->generator], but when @racket[s] has no further
|
|
values, the generator starts the sequence again (so that the
|
|
generator never stops producing values).}
|
|
|
|
@close-eval[generator-eval]
|