277 lines
11 KiB
Racket
277 lines
11 KiB
Racket
#lang scribble/doc
|
|
@(require "mz.ss"
|
|
(for-syntax scheme/base))
|
|
|
|
@(define-syntax speed
|
|
(syntax-rules ()
|
|
[(_ id what)
|
|
@t{An @scheme[id] application can provide better performance for
|
|
@elem[what]
|
|
iteration when it appears directly in a @scheme[for] clause.}]))
|
|
|
|
@title[#:tag "sequences"]{Sequences}
|
|
|
|
@guideintro["sequences"]{sequences}
|
|
|
|
A @deftech{sequence} encapsulates an ordered stream of values. The
|
|
elements of a sequence can be extracted with one of the @scheme[for]
|
|
syntactic forms or with the procedures returned by
|
|
@scheme[sequence-generate].
|
|
|
|
The sequence datatype overlaps with many other datatypes. Among
|
|
built-in datatypes, the sequence datatype includes the following:
|
|
|
|
@itemize{
|
|
|
|
@item{lists (see @secref["pairs"])}
|
|
|
|
@item{vectors (see @secref["vectors"])}
|
|
|
|
@item{hash tables (see @secref["hashtables"])}
|
|
|
|
@item{strings (see @secref["strings"])}
|
|
|
|
@item{byte strings (see @secref["bytestrings"])}
|
|
|
|
@item{input ports (see @secref["ports"])}
|
|
|
|
}
|
|
|
|
In addition, @scheme[make-do-sequence] creates a sequence given a
|
|
thunk that returns procedures to implement a generator, and the
|
|
@scheme[prop:sequence] property can be associated with a structure
|
|
type.
|
|
|
|
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 cause the bytes to be
|
|
read from the port.
|
|
|
|
Inidvidual 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.
|
|
|
|
@section{Sequence Predicate and Constructors}
|
|
|
|
@defproc[(sequence? [v any/c]) boolean?]{ Return @scheme[#t] if
|
|
@scheme[v] can be used as a sequence, @scheme[#f] otherwise.}
|
|
|
|
@defproc*[([(in-range [end number?]) sequence?]
|
|
[(in-range [start number?] [end number?] [step number? 1]) sequence?])]{
|
|
Returns a sequence whose elements are numbers. The single-argument
|
|
case @scheme[(in-range end)] is equivalent to @scheme[(in-range 0 end
|
|
1)]. The first number in the sequence is @scheme[start], and each
|
|
successive element is generated by adding @scheme[step] to the
|
|
previous element. The sequence stops before an element that would be
|
|
greater or equal to @scheme[end] if @scheme[step] is non-negative, or
|
|
less or equal to @scheme[end] if @scheme[step] is negative.
|
|
@speed[in-range "number"]}
|
|
|
|
@defproc[(in-naturals [start exact-nonnegative-integer? 0]) sequence?]{
|
|
Returns an infinite sequence of exact integers starting with
|
|
@scheme[start], where each element is one more than the preceeding
|
|
element. @speed[in-naturals "integer"]}
|
|
|
|
@defproc[(in-list [lst list?]) sequence?]{
|
|
Returns a sequence equivalent to @scheme[lst].
|
|
@speed[in-list "list"]}
|
|
|
|
@defproc[(in-vector [vec vector?]
|
|
[start exact-nonnegative-integer? 0]
|
|
[stop (or/c exact-nonnegative-integer? false/c) #f]
|
|
[step (and/c exact-integer? (not/c zero?)) 1])
|
|
sequence?]{
|
|
|
|
Returns a sequence equivalent to @scheme[vec] when no optional
|
|
arguments are supplied.
|
|
|
|
The optional arguments @scheme[start], @scheme[stop], and
|
|
@scheme[step] are analogous to @scheme[in-range], except that a
|
|
@scheme[#f] value for @scheme[stop] is equivalent to
|
|
@scheme[(vector-length vec)]. That is, the first element in the
|
|
sequence is @scheme[(vector-ref vec start)], and each successive
|
|
element is generated by adding @scheme[step] to index of the previous
|
|
element. The sequence stops before an index that would be greater or
|
|
equal to @scheme[end] if @scheme[step] is non-negative, or less or
|
|
equal to @scheme[end] if @scheme[step] is negative.
|
|
|
|
If @scheme[start] is less than @scheme[stop] and @scheme[step] is
|
|
negative, then the @exnraise[exn:fail:contract:mismatch]. Similarly,
|
|
if @scheme[start] is more than @scheme[stop] and @scheme[step] is
|
|
positive, then the @exnraise[exn:fail:contract:mismatch]. The
|
|
@scheme[start] and @scheme[stop] values are @emph{not} checked against
|
|
the size of @scheme[vec], so access can fail when an element is
|
|
demanded from the sequence.
|
|
|
|
@speed[in-vector "vector"]}
|
|
|
|
@defproc[(in-string [str string?]
|
|
[start exact-nonnegative-integer? 0]
|
|
[stop (or/c exact-nonnegative-integer? false/c) #f]
|
|
[step (and/c exact-integer? (not/c zero?)) 1])
|
|
sequence?]{
|
|
Returns a sequence equivalent to @scheme[str] when no optional
|
|
arguments are supplied.
|
|
|
|
The optional arguments @scheme[start], @scheme[stop], and
|
|
@scheme[step] are as in @scheme[in-vector].
|
|
|
|
@speed[in-string "string"]}
|
|
|
|
@defproc[(in-bytes [bstr bytes?]
|
|
[start exact-nonnegative-integer? 0]
|
|
[stop (or/c exact-nonnegative-integer? false/c) #f]
|
|
[step (and/c exact-integer? (not/c zero?)) 1])
|
|
sequence?]{
|
|
Returns a sequence equivalent to @scheme[bstr] when no optional
|
|
arguments are supplied.
|
|
|
|
The optional arguments @scheme[start], @scheme[stop], and
|
|
@scheme[step] are as in @scheme[in-vector].
|
|
|
|
@speed[in-bytes "byte string"]}
|
|
|
|
@defproc[(in-input-port-bytes [in input-port?]) sequence?]{
|
|
Returns a sequence equivalent to @scheme[in].}
|
|
|
|
@defproc[(in-input-port-chars [in input-port?]) sequence?]{ Returns a
|
|
sequence whose elements are read as characters form @scheme[in] (as
|
|
opposed to using @scheme[in] directly as a sequence to get bytes).}
|
|
|
|
@defproc[(in-lines [in input-port? (current-input-port)]
|
|
[mode (one-of 'linefeed 'return 'return-linefeed 'any 'any-one) 'any])
|
|
sequence?]{
|
|
|
|
Returns a sequence whose elements are the result of @scheme[(read-line
|
|
in mode)] until an end-of-line is encountered. Note that the default
|
|
mode is @scheme['any], whereas the default mode of @scheme[read-line]
|
|
is @scheme['linefeed].}
|
|
|
|
@defproc[(in-hash [hash hash?]) sequence?]{
|
|
Returns a sequence equivalent to @scheme[hash].}
|
|
|
|
@defproc[(in-hash-keys [hash hash?]) sequence?]{
|
|
Returns a sequence whose elements are the keys of @scheme[hash].}
|
|
|
|
@defproc[(in-hash-values [hash hash?]) sequence?]{
|
|
Returns a sequence whose elements are the values of @scheme[hash].}
|
|
|
|
@defproc[(in-hash-pairs [hash hash?]) sequence?]{
|
|
Returns a sequence whose elements are pairs, each containing a key and
|
|
its value from @scheme[hash] (as opposed to using @scheme[hash] directly
|
|
as a sequence to get the key and value as separate values for each
|
|
element).}
|
|
|
|
@defproc[(in-value [v any/c]) sequence]{
|
|
Returns a sequence that produces a single value: @scheme[v]. This form
|
|
is mostly useful for @scheme[let]-like bindings in forms such as
|
|
@scheme[for*/list].}
|
|
|
|
@defproc[(in-indexed [seq sequence?]) sequence?]{Returns a sequence
|
|
where each element has two values: the value produced by @scheme[seq],
|
|
and a non-negative exact integer starting with @scheme[0]. The
|
|
elements of @scheme[seq] must be single-valued.}
|
|
|
|
@defproc[(in-parallel [seq sequence?] ...) sequence?]{Returns a
|
|
sequence where each element has as many values as the number of
|
|
supplied @scheme[seq]s; the values, in order, are the values of each
|
|
@scheme[seq]. The elements of each @scheme[seq] must be single-valued.}
|
|
|
|
@defproc[(stop-before [seq sequence?] [pred (any/c . -> . any)])
|
|
sequence?]{ Returns a sequence that contains the elements of
|
|
@scheme[seq] (which must be single-valued), but only until the last
|
|
element for which applying @scheme[pred] to the element produces
|
|
@scheme[#t], after which the sequence ends.}
|
|
|
|
@defproc[(stop-after [seq sequence?] [pred (any/c . -> . any)])
|
|
sequence?]{ Returns a sequence that contains the elements of
|
|
@scheme[seq] (which must be single-valued), but only until the element
|
|
(inclusive) for which applying @scheme[pred] to the element produces
|
|
@scheme[#t], after which the sequence ends.}
|
|
|
|
@defproc[(make-do-sequence [thunk (-> (values (any/c . -> . any)
|
|
(any/c . -> . any/c)
|
|
any/c
|
|
(any/c . -> . any/c)
|
|
(() () #:rest list? . ->* . any/c)
|
|
((any/c) () #:rest list? . ->* . any/c)))])
|
|
sequence?]{
|
|
|
|
Returns a sequence whose elements are generated by the procedures and
|
|
initial value returned by the thunk. The generator 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 @scheme[thunk] results define the generated elements as follows:
|
|
|
|
@itemize{
|
|
|
|
@item{The first result is a @scheme[_pos->element] procedure that takes
|
|
the current position and returns the value(s) for the current element.}
|
|
|
|
@item{The second result is a @scheme[_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 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).}
|
|
|
|
@item{The fifth result is like the fourth result, but it takes the
|
|
current element value(s) instead of the current position.}
|
|
|
|
@item{The sixth result is like the fourth result, but it takes both
|
|
the current position and the current element values(s) and
|
|
determines a sequence end after the current element is already
|
|
included in the sequence.}
|
|
|
|
}
|
|
|
|
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 @scheme[#f], the sequence ends, and none are called
|
|
again. Typically, one of the functions determines the end condition,
|
|
and the other two functions always return @scheme[#t].}
|
|
|
|
@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 @scheme[v] is an instance of
|
|
a structure type with this property, then @scheme[(sequence? v)]
|
|
produces @scheme[#t].
|
|
|
|
@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)
|
|
]}
|
|
|
|
@section{Sequence Generators}
|
|
|
|
@defproc[(sequence-generate [seq sequence?]) (values (-> boolean?)
|
|
(-> any))]{
|
|
Returns two thunks to extract elements from the sequence. The first
|
|
returns @scheme[#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].}
|
|
|