Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions lib/elixir/lib/enum.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1855,6 +1855,27 @@ defmodule Enum do
do_split_reverse(reverse(collection), abs(count), [])
end

@doc """
Chunks the `collection` each time `fun` returns `true` or at the beginning of
`collection`.

## Examples

iex> Enum.slice_before([1, 2, 3, 6, 7, 9], fn(x) -> rem(x, 3) == 0 end)
[[1, 2], [3], [6, 7], [9]]

"""
@spec slice_before(t, (element -> any)) :: [list]
def slice_before(collection, fun) do
{_, {acc, res}} =
Enumerable.reduce(collection, {:cont, {[], []}}, R.slice_before(fun))

case res do
[] -> []
buffer -> :lists.reverse([:lists.reverse(buffer) | acc])
end
end

@doc """
Splits `collection` in two while `fun` returns `true`.

Expand Down
27 changes: 27 additions & 0 deletions lib/elixir/lib/stream.ex
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,33 @@ defmodule Stream do
lazy enum, acc, fn(f1) -> R.scan_3(fun, f1) end
end

@doc """
Chunks the `enum` each time `fun` returns `true` or at the beginning of
`enum`.

## Examples

iex> stream = Stream.slice_before(1..8, &(rem(&1, 3) == 0))
iex> Enum.to_list(stream)
[[1,2], [3, 4, 5], [6, 7, 8]]

"""
@spec slice_before(Enumerable.t, (element -> any)) :: Enumerable.t
def slice_before(enum, fun) do
lazy enum,
[],
fn(f1) -> R.slice_before(fun, f1) end,
&do_slice_before(&1, &2)
end

defp do_slice_before(acc(_, nil, _) = acc, _) do
{:cont, acc}
end

defp do_slice_before(acc(h, buffer, t), f1) do
cont_with_acc(f1, :lists.reverse(buffer), h, nil, t)
end

@doc """
Lazily takes the next `n` items from the enumerable and stops
enumeration.
Expand Down
15 changes: 15 additions & 0 deletions lib/elixir/lib/stream/reducers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,21 @@ defmodule Stream.Reducers do
end
end

defmacro slice_before(callback, f \\ nil) do
quote do
fn
entry, acc(h, [], t) ->
{:cont, acc(h, [entry], t)}
entry, acc(h, buffer, t) ->
if unquote(callback).(entry) do
cont_with_acc(unquote(f), :lists.reverse(buffer), h, [entry], t)
else
{:cont, acc(h, [entry|buffer], t)}
end
end
end
end

defmacro take(f \\ nil) do
quote do
fn(entry, acc(h, n, t) = orig) ->
Expand Down
14 changes: 14 additions & 0 deletions lib/elixir/test/elixir/enum_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,14 @@ defmodule EnumTest.List do
assert Enum.split([1, 2, 3], -10) == {[], [1, 2, 3]}
end

test :slice_before do
assert Enum.slice_before([1, 2, 2, 3, 4, 4, 6, 7, 7], &(rem(&1, 2) == 1)) == [[1, 2, 2], [3, 4, 4, 6], [7], [7]]
assert Enum.slice_before([1, 2, 3, 4], fn _ -> false end) == [[1, 2, 3, 4]]
assert Enum.slice_before([1, 2, 3, 4], fn _ -> true end) == [[1], [2], [3], [4]]
assert Enum.slice_before([], fn _ -> true end) == []
assert Enum.slice_before([1], fn _ -> true end) == [[1]]
end

test :split_while do
assert Enum.split_while([1, 2, 3], fn(_) -> false end) == {[], [1, 2, 3]}
assert Enum.split_while([1, 2, 3], fn(_) -> true end) == {[1, 2, 3], []}
Expand Down Expand Up @@ -1006,6 +1014,12 @@ defmodule EnumTest.Range do
assert Enum.split(range, 3) == {[1, 0], []}
end

test :slice_before do
assert Enum.slice_before(1..4, fn _ -> false end) == [[1, 2, 3, 4]]
assert Enum.slice_before(1..4, fn _ -> true end) == [[1], [2], [3], [4]]
assert Enum.slice_before(1..4, &(rem(&1, 2) == 1)) == [[1, 2], [3, 4]]
end

test :split_while do
range = 1..3
assert Enum.split_while(range, fn(_) -> false end) == {[], [1, 2, 3]}
Expand Down
16 changes: 16 additions & 0 deletions lib/elixir/test/elixir/stream_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,22 @@ defmodule StreamTest do
assert Stream.scan([], 0, &(&1 + &2)) |> Enum.to_list == []
end

test "slice_before/2" do
stream = Stream.slice_before([1, 2, 1, 1, 2, 3, 4, 1, 2, 3], &(&1 == 1))

assert is_lazy(stream)
assert Enum.to_list(stream) ==
[[1, 2], [1], [1, 2, 3, 4], [1, 2, 3]]
assert stream |> Stream.take(3) |> Enum.to_list ==
[[1, 2], [1], [1, 2, 3, 4]]
end

test "slice_before/2 is zippable" do
stream = Stream.slice_before([1, 2, 2, 3], &(&1 == 2))
list = Enum.to_list(stream)
assert Enum.zip(list, list) == Enum.zip(stream, stream)
end

test "take/2" do
stream = Stream.take(1..1000, 5)
assert is_lazy(stream)
Expand Down