Streams
A number of the data structures in PureFun.jl rely on lazy evaluation and lazily evaluated lists, which are provided by PureFun.Lazy. Here we look at some toy examples to get a feel for to use Streams.
using PureFun
using PureFun.Lazy: Stream, @stream
Basics
"Lazy evaluation" describes a strategy for evaluating expressions, and has two main features:
The evaluation of the expression is delayed (suspended) until its result is needed
The result is cached (memoized) the first time the expression is evaluated, so that subsequent evaluations become cheap lookups
Streams are lazily evaluated lists, and are described in section 4.2 of the book:
Streams (also known as lazy lists) are similar to ordinary lists, except that every cell is systematically suspended
integers = Stream(Iterators.countfrom(1))
PureFun.Lazy.NonEmpty{Int64}
1
2
3
4
5
6
7
...
The @stream
macro enables you to build streams by providing an expression which evaluates to the first element, and one that evaluates to a stream of remaining elements. Both expressions are suspended, and only evaluated when the value is needed (at which time, the value is cached).
fibonacci(a,b) = @stream(Int, a, fibonacci(b, a+b))
fibonacci(0, 1)
PureFun.Lazy.NonEmpty{Int64}
0
1
1
2
3
5
8
...
Comparison to iterators
Like Streams, Julia's iterators are also lazily evaluated. The main difference is that Streams are memoized, meaning that values that have been calculated are cached and can be revisited without having to recalculate them.
Like all of the data structures in PureFun.jl, Streams are iterators themselves, and calling a function from Base.Iterators
on a Stream works as expected. Calling Stream
on an iterator, on the other hand, is kind of like a lazy collect
, it materializes computed values as they are iterated out:
using Base.Iterators: zip, drop, take
foo = map(x -> 2x, accumulate(+, filter(isodd, integers), init=0.0)) |> Stream
bar = zip(foo, drop(foo, 1)) |> Stream
collect(take(bar, 7))
7-element Vector{Tuple{Float64, Float64}}:
(2.0, 8.0)
(8.0, 18.0)
(18.0, 32.0)
(32.0, 50.0)
(50.0, 72.0)
(72.0, 98.0)
(98.0, 128.0)
We can access elements of streams the same way we would do for regular (eager) lists:
bar[2000]
(8.0e6, 8.008002e6)
Reference
PureFun.Lazy.Stream
— TypeStream{T} <: PureFun.PFStream{T}
Stream with elements of type T
. Every cell in a stream is systematically suspended, and only evaluated when the value in that cell is required. Furthermore, the value is cached the first time a cell is evaluated, so that subsequent accesses are cheap. Introduced in $\S{4.2}$
PureFun.Lazy.@stream
— Macro@stream T headexpr tailexpr
this macro makes it more convenient to construct a stream
This page was generated using Literate.jl.