Skip to content

Commit b570850

Browse files
author
José Valim
committed
Fix Stream.cycle/1 with inner halting stream, closes #2285
1 parent 5882cf5 commit b570850

File tree

2 files changed

+23
-7
lines changed

2 files changed

+23
-7
lines changed

lib/elixir/lib/stream.ex

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -889,8 +889,9 @@ defmodule Stream do
889889
@spec cycle(Enumerable.t) :: Enumerable.t
890890
def cycle(enumerable) do
891891
fn acc, fun ->
892-
reduce = &Enumerable.reduce(enumerable, &1, fun)
893-
do_cycle(reduce, reduce, acc)
892+
inner = &do_cycle_each(&1, &2, fun)
893+
outer = &Enumerable.reduce(enumerable, &1, inner)
894+
do_cycle(outer, outer, acc)
894895
end
895896
end
896897

@@ -903,16 +904,26 @@ defmodule Stream do
903904
end
904905

905906
defp do_cycle(reduce, cycle, acc) do
906-
case reduce.(acc) do
907-
{:done, acc} ->
908-
do_cycle(cycle, cycle, {:cont, acc})
909-
{:halted, acc} ->
907+
try do
908+
reduce.(acc)
909+
catch
910+
{:stream_cycle, acc} ->
910911
{:halted, acc}
912+
else
913+
{state, acc} when state in [:done, :halted] ->
914+
do_cycle(cycle, cycle, {:cont, acc})
911915
{:suspended, acc, continuation} ->
912916
{:suspended, acc, &do_cycle(continuation, cycle, &1)}
913917
end
914918
end
915919

920+
defp do_cycle_each(x, acc, f) do
921+
case f.(x, acc) do
922+
{:halt, h} -> throw({:stream_cycle, h})
923+
{_, _} = o -> o
924+
end
925+
end
926+
916927
@doc """
917928
Emit a sequence of values, starting with `start_value`. Successive
918929
values are generated by calling `next_fun` on the previous value.
@@ -1049,7 +1060,7 @@ defmodule Stream do
10491060

10501061
defp do_unfold(next_acc, next_fun, {:cont, acc}, fun) do
10511062
case next_fun.(next_acc) do
1052-
nil -> {:done, acc}
1063+
nil -> {:done, acc}
10531064
{v, next_acc} -> do_unfold(next_acc, next_fun, fun.(v, acc), fun)
10541065
end
10551066
end

lib/elixir/test/elixir/stream_test.exs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ defmodule StreamTest do
120120
assert Enum.zip(1..6, [1,2,3,1,2,3]) == Enum.zip(1..6, stream)
121121
end
122122

123+
test "cycle/1 with inner stream" do
124+
assert [1,2,3] |> Stream.take(2) |> Stream.cycle |> Enum.take(4) ==
125+
[1,2,1,2]
126+
end
127+
123128
test "drop/2" do
124129
stream = Stream.drop(1..10, 5)
125130
assert is_lazy(stream)

0 commit comments

Comments
 (0)