Creating infinite sequences with Stream.unfold/2
Notes
Creating infinite sequences daily?
Me neither.
But it’s really cool to see how we can do that with Stream.unfold/2
. And
save that knowledge in some part of your brain for the day you need it.
You can think of unfold as something like the opposite of reduce.
When we reduce, we grab a list (or sequence) and iterate over the values to generate a final value. With unfold, we start with a single value (the seed) and then generate a sequence by applying a transformation on each subsequent value.
Finite sequence
We can use Stream.unfold/2
to generate a finite sequence. If we return nil
the generation will stop.
stream = Stream.unfold(0, fn
10 -> nil
i -> {i, i + 1}
end)
stream |> Enum.to_list()
#=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Infinite sequence
To generate an infinite sequence, we can modify the previous sequence to remove
the 10 -> nil
pattern match.
infinite_stream = Stream.unfold(0, fn
i -> {i, i + 1}
end)
infinite_stream |> Enum.take(10)
#=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Note how we use Enum.take/2
instead of to_list/1
so we can create the first
10 elements of the sequence.
Fibonacci sequence
fibonacci = Stream.unfold(0, fn
0 -> {0, 1}
1 -> {1, {1, 1}}
{1, 1} -> {1, {1, 2}}
{ix, iy} -> {iy, {iy, ix + iy}}
end)
fibonacci |> Enum.take(12)
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
An even better version of the fibonacci sequence (not shown on the video) would be like this:
fibonacci = Stream.unfold(0, fn
0 -> {0, {0, 1}}
{ix, iy} -> {iy, {iy, ix + iy}}
end)
fibonacci |> Enum.take(12)
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]