Skip to content

Commit 429fc20

Browse files
author
José Valim
committed
Merge pull request #1693 from pminten/stream-generate
Add Stream.unfold
2 parents e8c3ed2 + 7b1b0d9 commit 429fc20

File tree

2 files changed

+38
-0
lines changed

2 files changed

+38
-0
lines changed

lib/elixir/lib/stream.ex

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,31 @@ defmodule Stream do
427427
acc: true]
428428
end
429429

430+
@doc """
431+
Emit a sequence of values and accumulators. Successive values are generated by
432+
calling `next_fun` with the previous accumulator.
433+
434+
If the return value is nil iteration ends.
435+
436+
## Examples
437+
438+
iex> Stream.unfold(5, fn 0 -> nil; n -> {n, n-1} end) |> Enum.to_list()
439+
[5, 4, 3, 2, 1]
440+
"""
441+
@spec unfold(acc, (acc -> { element, acc } | nil)) :: t
442+
def unfold(acc, f) do
443+
fn acc1, f1 ->
444+
do_unfold(acc, f, acc1, f1)
445+
end
446+
end
447+
448+
defp do_unfold(gen_acc, gen_fun, acc, fun) do
449+
case gen_fun.(gen_acc) do
450+
nil -> acc
451+
{ v, new_gen_acc } -> do_unfold(new_gen_acc, gen_fun, fun.(v, acc), fun)
452+
end
453+
end
454+
430455
@doc """
431456
Creates a stream where each item in the enumerable will
432457
be accompanied by its index.

lib/elixir/test/elixir/stream_test.exs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,19 @@ defmodule StreamTest do
175175
stream = Stream.drop(1..100, 5)
176176
assert Stream.take_while(stream, &(&1 < 11)) |> Enum.to_list == [6,7,8,9,10]
177177
end
178+
179+
test :unfold do
180+
stream = Stream.unfold(10, fn x -> if x > 0, do: {x, x-1}, else: nil end)
181+
assert Enum.take(stream, 5) == [10, 9, 8, 7, 6]
182+
stream = Stream.unfold(5, fn x -> if x > 0, do: {x, x-1}, else: nil end)
183+
assert Enum.to_list(stream) == [5, 4, 3, 2, 1]
184+
185+
# Only calculate values if needed
186+
stream = Stream.unfold(1, fn x -> if x > 0, do: {x, x-1}, else: throw(:boom) end)
187+
assert Enum.take(stream, 1) == [1]
188+
stream = Stream.unfold(5, fn x -> if x > 0, do: {x, x-1}, else: nil end)
189+
assert Enum.to_list(Stream.take(stream, 2)) == [5, 4]
190+
end
178191

179192
test :with_index do
180193
stream = Stream.with_index([1,2,3])

0 commit comments

Comments
 (0)