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

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 100000)]) (values i (add1 i))))
> (time (for ([(a b) (in-stream s)]) (void)))

cpu time: 104 real time: 61 gc time: 49

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

cpu time: 24 real time: 17 gc time: 14

; 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 100000)]) (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: 26 real time: 26 gc time: 0

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

cpu time: 45 real time: 45 gc time: 0