stream-values
stream-cons/  values
stream/  values
stream*/  values
for/  stream/  values
for*/  stream/  values
unsafe-in-stream
7.8

stream-values

Sorawee Porncharoenwase <sorawee.pwase@gmail.com>

 (require stream-values) package: stream-values

This library allows manipulation of multiple values in streams. The for/stream/values form, in particular, could be used to construct a relatively efficient sequence of multiple values in the traditional (3m) variant of Racket (as generators are highly inefficient in this variant).

syntax

(stream-cons/values first-expr rest-expr)

syntax

(stream/values expr ...)

syntax

(stream*/values expr ...)

syntax

(for/stream/values (for-clause ...) body-or-break ... body)

syntax

(for*/stream/values (for-clause ...) body-or-break ... body)

Like stream-cons, stream, stream*, for/stream, and for*/stream, but they support multiple values.

Examples:
> (define s (stream-cons/values (values 1 2) empty-stream))
> (stream-first s)

1

2

> (define t (stream/values (values 1 2) (values 3 4)))
> (for/list ([(left right) (in-stream t)])
    (list left right))

'((1 2) (3 4))

> (define u
    (for/stream/values ([i (in-naturals)])
      (values i (add1 i))))
> (for/list ([(left right) u] [_ 5])
    (list left right))

'((0 1) (1 2) (2 3) (3 4) (4 5))

procedure

(unsafe-in-stream s)  sequence?

  s : stream?
Similar to in-stream (which supports multiple values already), but it cooperates with this library so that an unsafe-in-stream application can provide better performance for iteration on streams (that are constructed via this library) when unsafe-in-stream appears directly in a for clause. It is unsafe in a sense that stream memoization (which is a feature of Racket streams) is not guaranteed. That is, for each element in the stream, an iteration via unsafe-in-stream might or might not memoize the element. However, it does guarantee that if a stream is fully memoized, iterating on the stream will use the memoized result, though in this case, in-stream will provide a better performance.

This procedure is useful when a stream is used in the iteration only once and then discarded, since memoization does not matter and the iteration could be significantly faster. On the other hand, if the stream will be used again in the future, the lack of memoization could result in a performance loss or even a surprisingly incorrect result.

See for for information on the reachability of stream elements during an iteration.

Examples:
; Performance of in-stream vs unsafe-in-stream
> (define s (for/stream/values ([i (in-range 1000000)]) (values i (add1 i))))
> (time (for ([(a b) (in-stream s)]) (void)))

cpu time: 1263 real time: 1264 gc time: 765

> (define t (for/stream/values ([i (in-range 1000000)]) (values i (add1 i))))
> (time (for ([(a b) (unsafe-in-stream t)]) (void)))

cpu time: 160 real time: 160 gc time: 34

; Lack of memoization
> (define xs (for/stream/values ([i (in-range 5)]) (displayln i)))
> (for ([_ (in-stream xs)]) (void))

0

1

2

3

4

; This iteration should not display any element because the stream is memoized.
> (for ([_ (in-stream xs)]) (void))
> (define ys (for/stream/values ([i (in-range 5)]) (displayln i)))
> (for ([_ (unsafe-in-stream ys)]) (void))

0

1

2

3

4

; Due to the lack of memoization, this iteration displays elements again.
> (for ([_ (unsafe-in-stream ys)]) (void))

0

1

2

3

4

; Fully memoization stream is utilized
> (define zs (for/stream/values ([i (in-range 5)]) (displayln i)))
> (for ([_ (in-stream zs)]) (void))

0

1

2

3

4

; This iteration should not display any element because the stream is fully memoized.
> (for ([_ (unsafe-in-stream zs)]) (void))

; Performance of in-stream vs unsafe-in-stream on fully memoized stream.
> (define w (for/stream/values ([i (in-range 1000000)]) (values i (add1 i))))
; Fully memoize w first.
> (for ([(a b) (in-stream w)]) (void))
> (time (for ([(a b) (in-stream w)]) (void)))

cpu time: 283 real time: 284 gc time: 0

> (time (for ([(a b) (unsafe-in-stream w)]) (void)))

cpu time: 391 real time: 392 gc time: 0