Skip to content

Commit 1b93904

Browse files
author
José Valim
committed
Support Stream.drop/2 with negative count
1 parent 303789f commit 1b93904

File tree

2 files changed

+80
-2
lines changed

2 files changed

+80
-2
lines changed

lib/elixir/lib/stream.ex

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,18 +154,51 @@ defmodule Stream do
154154
@doc """
155155
Lazily drops the next `n` items from the enumerable.
156156
157+
If a negative `n` is given, it will drop the last `n` items from
158+
the collection. Note that the mechanism by which this is implemented
159+
will delay the emission of any item until `n` additional items have
160+
been emitted by the enum.
161+
157162
## Examples
158163
159164
iex> stream = Stream.drop(1..10, 5)
160165
iex> Enum.to_list(stream)
161166
[6,7,8,9,10]
162167
168+
iex> stream = Stream.drop(1..10, -5)
169+
iex> Enum.to_list(stream)
170+
[1,2,3,4,5]
171+
163172
"""
164173
@spec drop(Enumerable.t, non_neg_integer) :: Enumerable.t
165174
def drop(enum, n) when n >= 0 do
166175
lazy enum, n, fn(f1) -> R.drop(f1) end
167176
end
168177

178+
def drop(enum, n) when n < 0 do
179+
n = abs(n)
180+
181+
lazy enum, { 0, [], [] }, fn(f1) ->
182+
fn
183+
entry, [h, { count, buf1, [] } | t] ->
184+
do_drop(:cont, n, entry, h, count, buf1, [], t)
185+
entry, [h, { count, buf1, [next|buf2] } | t] ->
186+
{ reason, [h|t] } = f1.(next, [h|t])
187+
do_drop(reason, n, entry, h, count, buf1, buf2, t)
188+
end
189+
end
190+
end
191+
192+
defp do_drop(reason, n, entry, h, count, buf1, buf2, t) do
193+
buf1 = [entry|buf1]
194+
count = count + 1
195+
if count == n do
196+
{ reason, [h, { 0, [], :lists.reverse(buf1) }|t] }
197+
else
198+
{ reason, [h, { count, buf1, buf2 }|t] }
199+
end
200+
end
201+
169202
@doc """
170203
Lazily drops elements of the enumerable while the given
171204
function returns true.
@@ -364,7 +397,7 @@ defmodule Stream do
364397
&do_take(enum, abs(n), &1, &2)
365398
end
366399

367-
def do_take(enum, n, acc, f) do
400+
defp do_take(enum, n, acc, f) do
368401
{ _, { _count, buf1, buf2 } } =
369402
Enumerable.reduce(enum, { :cont, { 0, [], [] } }, fn
370403
entry, { count, buf1, buf2 } ->
@@ -373,7 +406,7 @@ defmodule Stream do
373406
if count == n do
374407
{ :cont, { 0, [], buf1 } }
375408
else
376-
{ :cont, { count, buf1, buf2} }
409+
{ :cont, { count, buf1, buf2 } }
377410
end
378411
end)
379412

lib/elixir/test/elixir/stream_test.exs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,36 @@ defmodule StreamTest do
9393
assert Stream.drop(nats, 2) |> Enum.take(5) == [3,4,5,6,7]
9494
end
9595

96+
test "drop with negative count" do
97+
stream = Stream.drop(1..10, -5)
98+
assert is_lazy(stream)
99+
assert Enum.to_list(stream) == [1,2,3,4,5]
100+
101+
stream = Stream.drop(1..10, -5)
102+
list = Enum.to_list(stream)
103+
assert Enum.zip(list, list) == Enum.zip(stream, stream)
104+
end
105+
106+
test "drop with negative count stream entries" do
107+
par = self
108+
pid = spawn_link fn ->
109+
Enum.each Stream.drop(&inbox_stream/2, -3),
110+
fn x -> par <- { :stream, x } end
111+
end
112+
113+
pid <- { :stream, 1 }
114+
pid <- { :stream, 2 }
115+
pid <- { :stream, 3 }
116+
refute_receive { :stream, 1 }
117+
118+
pid <- { :stream, 4 }
119+
assert_receive { :stream, 1 }
120+
121+
pid <- { :stream, 5 }
122+
assert_receive { :stream, 2 }
123+
refute_receive { :stream, 3 }
124+
end
125+
96126
test "drop_while" do
97127
stream = Stream.drop_while(1..10, &(&1 <= 5))
98128
assert is_lazy(stream)
@@ -316,4 +346,19 @@ defmodule StreamTest do
316346
defp is_lazy(stream) do
317347
is_record(stream, Stream.Lazy) or is_function(stream, 2)
318348
end
349+
350+
defp inbox_stream({ :suspend, acc }, f) do
351+
{ :suspended, acc, &inbox_stream(&1, f) }
352+
end
353+
354+
defp inbox_stream({ :halt, acc }, _f) do
355+
{ :halted, acc }
356+
end
357+
358+
defp inbox_stream({ :cont, acc }, f) do
359+
receive do
360+
{ :stream, item } ->
361+
inbox_stream(f.(item, acc), f)
362+
end
363+
end
319364
end

0 commit comments

Comments
 (0)