Skip to content

Commit d454aaf

Browse files
committed
Fix Stream bugs and add more composing tests
Fix Stream.take_while looping infinitely when given lazy stream. Fix Stream.take and Stream.take_while calling fun one extra time. Add more tests where Stream functions are given streams.
1 parent a154a9e commit d454aaf

File tree

2 files changed

+39
-14
lines changed

2 files changed

+39
-14
lines changed

lib/elixir/lib/stream.ex

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ defmodule Stream do
2727
We say the functions in `Stream` are *lazy* and the functions in `Enum`
2828
are *eager*.
2929
30-
Due to their laziness, streams are useful when working with large
30+
Due to their laziness, streams are useful when working with large
3131
(or even infinite) collections. When chaining many operations with `Enum`,
3232
intermediate lists are created, while `Stream` creates a recipe of
3333
computations that are executed at a later moment. Let's see another
@@ -298,7 +298,7 @@ defmodule Stream do
298298
299299
"""
300300
@spec repeatedly((() -> element)) :: t
301-
def repeatedly(generator_fun)
301+
def repeatedly(generator_fun)
302302
when is_function(generator_fun, 0) do
303303
do_repeatedly(generator_fun, &1, &2)
304304
end
@@ -323,14 +323,14 @@ defmodule Stream do
323323
324324
"""
325325
@spec take(Enumerable.t, non_neg_integer) :: t
326-
def take(enumerable, n) when n >= 0 do
326+
def take(_enumerable, 0), do: Lazy[enumerable: [], fun: & &1]
327+
328+
def take(enumerable, n) when n > 0 do
327329
Lazy[enumerable: enumerable,
328330
fun: fn(f1) ->
329-
fn
330-
(entry, { acc, n }) when n > 0 ->
331-
{ f1.(entry, acc), n-1 }
332-
(_entry, acc) ->
333-
throw { :stream_lazy, acc }
331+
fn(entry, { acc, n }) ->
332+
acc = { f1.(entry, acc), n-1 }
333+
if n > 1, do: acc, else: throw { :stream_lazy, acc }
334334
end
335335
end,
336336
acc: n]
@@ -351,11 +351,12 @@ defmodule Stream do
351351
def take_while(enumerable, f) do
352352
Lazy[enumerable: enumerable,
353353
fun: fn(f1) ->
354-
fn
355-
entry, { acc, true } ->
356-
if f.(entry), do: { f1.(entry, acc), true }, else: { acc, false }
357-
_entry, acc ->
358-
acc
354+
fn(entry, { acc, true }) ->
355+
if f.(entry) do
356+
{ f1.(entry, acc), true }
357+
else
358+
throw { :stream_lazy, { acc, false } }
359+
end
359360
end
360361
end,
361362
acc: true]

lib/elixir/test/elixir/stream_test.exs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ defmodule StreamTest do
4040

4141
assert Enum.to_list(Stream.drop(1..5, 0)) == [1,2,3,4,5]
4242
assert Enum.to_list(Stream.drop(1..3, 5)) == []
43+
44+
nats = Stream.iterate(1, &(&1 + 1))
45+
assert Stream.drop(nats, 2) |> Enum.take(5) == [3,4,5,6,7]
4346
end
4447

4548
test :drop_while do
@@ -49,12 +52,18 @@ defmodule StreamTest do
4952

5053
assert Enum.to_list(Stream.drop_while(1..5, &1 <= 0)) == [1,2,3,4,5]
5154
assert Enum.to_list(Stream.drop_while(1..3, &1 <= 5)) == []
55+
56+
nats = Stream.iterate(1, &(&1 + 1))
57+
assert Stream.drop_while(nats, &1 <= 5) |> Enum.take(5) == [6,7,8,9,10]
5258
end
5359

5460
test :filter do
5561
stream = Stream.filter([1,2,3], fn(x) -> rem(x, 2) == 0 end)
5662
assert is_lazy(stream)
5763
assert Enum.to_list(stream) == [2]
64+
65+
nats = Stream.iterate(1, &(&1 + 1))
66+
assert Stream.filter(nats, &(rem(&1, 2) == 0)) |> Enum.take(5) == [2,4,6,8,10]
5867
end
5968

6069
test :iterate do
@@ -72,15 +81,21 @@ defmodule StreamTest do
7281
stream = Stream.map([1,2,3], &1 * 2)
7382
assert is_lazy(stream)
7483
assert Enum.to_list(stream) == [2,4,6]
84+
85+
nats = Stream.iterate(1, &(&1 + 1))
86+
assert Stream.map(nats, &(&1 * 2)) |> Enum.take(5) == [2,4,6,8,10]
7587
end
7688

7789
test :reject do
7890
stream = Stream.reject([1,2,3], fn(x) -> rem(x, 2) == 0 end)
7991
assert is_lazy(stream)
8092
assert Enum.to_list(stream) == [1,3]
93+
94+
nats = Stream.iterate(1, &(&1 + 1))
95+
assert Stream.reject(nats, &(rem(&1, 2) == 0)) |> Enum.take(5) == [1,3,5,7,9]
8196
end
8297

83-
test :repeatedly do
98+
test :repeatedly do
8499
stream = Stream.repeatedly(fn -> 1 end)
85100
assert Enum.take(stream, 5) == [1,1,1,1,1]
86101
stream = Stream.repeatedly(&:random.uniform/0)
@@ -95,6 +110,9 @@ defmodule StreamTest do
95110

96111
assert Enum.to_list(Stream.take(1..1000, 0)) == []
97112
assert Enum.to_list(Stream.take(1..3, 5)) == [1,2,3]
113+
114+
nats = Stream.iterate(1, &(&1 + 1))
115+
assert Enum.to_list(Stream.take(nats, 5)) == [1,2,3,4,5]
98116
end
99117

100118
test :take_while do
@@ -104,12 +122,18 @@ defmodule StreamTest do
104122

105123
assert Enum.to_list(Stream.take_while(1..1000, &1 <= 0)) == []
106124
assert Enum.to_list(Stream.take_while(1..3, &1 <= 5)) == [1,2,3]
125+
126+
nats = Stream.iterate(1, &(&1 + 1))
127+
assert Enum.to_list(Stream.take_while(nats, &(&1 <= 5))) == [1,2,3,4,5]
107128
end
108129

109130
test :with_index do
110131
stream = Stream.with_index([1,2,3])
111132
assert is_lazy(stream)
112133
assert Enum.to_list(stream) == [{1,0},{2,1},{3,2}]
134+
135+
nats = Stream.iterate(1, &(&1 + 1))
136+
assert Stream.with_index(nats) |> Enum.take(3) == [{1,0},{2,1},{3,2}]
113137
end
114138

115139
defp is_lazy(stream) do

0 commit comments

Comments
 (0)