Skip to content

Commit 1aca6db

Browse files
author
José Valim
committed
Merge pull request #1660 from betawaffle/enum-concat
Add Stream.concat/1,2 and Enum.concat/1,2
2 parents 5f1e5eb + f7391b4 commit 1aca6db

File tree

16 files changed

+154
-40
lines changed

16 files changed

+154
-40
lines changed

lib/elixir/lib/enum.ex

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,52 @@ defmodule Enum do
221221
end
222222
end
223223

224+
@doc """
225+
Given an enumerable of enumerables, concatenate the enumerables into a single list.
226+
227+
## Examples
228+
229+
iex> Enum.concat([1..3, 4..6, 7..9])
230+
[1,2,3,4,5,6,7,8,9]
231+
232+
iex> Enum.concat([[1, [2], 3], [4], [5, 6]])
233+
[1,[2],3,4,5,6]
234+
235+
"""
236+
@spec concat(t) :: t
237+
def concat(enumerables) do
238+
do_concat(enumerables)
239+
end
240+
241+
@doc """
242+
Concatenates the enumerable on the right with the enumerable on the left.
243+
244+
This function produces the same result the `++` operator for lists.
245+
246+
## Examples
247+
248+
iex> Enum.concat(1..3, 4..6)
249+
[1,2,3,4,5,6]
250+
251+
iex> Enum.concat([1, 2, 3], [4, 5, 6])
252+
[1,2,3,4,5,6]
253+
254+
"""
255+
@spec concat(t, t) :: t
256+
def concat(left, right) when is_list(left) and is_list(right) do
257+
left ++ right
258+
end
259+
260+
def concat(left, right) do
261+
do_concat([left, right])
262+
end
263+
264+
defp do_concat(enumerable, acc // []) do
265+
fun = &[&1|&2]
266+
Enumerable.reduce(enumerable, acc, &Enumerable.reduce(&1, &2, fun))
267+
|> :lists.reverse
268+
end
269+
224270
@doc """
225271
Drops the first `count` items from `collection`.
226272
Expects an ordered collection.

lib/elixir/lib/kernel/cli.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ defmodule Kernel.CLI do
342342
:filelib.ensure_dir(:filename.join(config.output, "."))
343343

344344
files = Enum.map patterns, Path.wildcard(&1)
345-
files = Enum.uniq(List.concat(files))
345+
files = Enum.uniq(Enum.concat(files))
346346
files = Enum.filter files, :filelib.is_regular(&1)
347347

348348
if files != [] do

lib/elixir/lib/list.ex

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,15 @@ defmodule List do
1212

1313
@compile :inline_list_funcs
1414

15-
@doc """
16-
Given a list of lists, concatenate the sublists into a single list.
17-
18-
## Examples
19-
20-
iex> List.concat([[1, [2], 3], [4], [5, 6]])
21-
[1,[2],3,4,5,6]
22-
23-
"""
15+
@doc false
2416
def concat(list) when is_list(list) do
17+
IO.write "List.concat/1 is deprecated, please use Enum.concat/1 instead\n#{Exception.format_stacktrace}"
2518
:lists.append(list)
2619
end
2720

28-
@doc """
29-
Concatenates the list on the right with the list on the left.
30-
31-
This function produces the same result the `++` operator.
32-
33-
## Examples
34-
35-
iex> List.concat([1, 2, 3], [4, 5, 6])
36-
[1,2,3,4,5,6]
37-
38-
"""
21+
@doc false
3922
def concat(list, elements) when is_list(list) and is_list(elements) do
23+
IO.write "List.concat/2 is deprecated, please use Enum.concat/2 instead\n#{Exception.format_stacktrace}"
4024
list ++ elements
4125
end
4226

lib/elixir/lib/stream.ex

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,46 @@ defmodule Stream do
131131
@type index :: non_neg_integer
132132
@type default :: any
133133

134+
@doc """
135+
Creates a stream that enumerates each enumerable in an enumerable.
136+
137+
## Examples
138+
139+
iex> stream = Stream.concat([1..3, 4..6, 7..9])
140+
iex> Enum.to_list(stream)
141+
[1,2,3,4,5,6,7,8,9]
142+
143+
"""
144+
@spec concat(Enumerable.t) :: t
145+
def concat(enumerables) do
146+
&do_concat(enumerables, &1, &2)
147+
end
148+
149+
@doc """
150+
Creates a stream that enumerates the first argument, followed by the second.
151+
152+
## Examples
153+
154+
iex> stream = Stream.concat(1..3, 4..6)
155+
iex> Enum.to_list(stream)
156+
[1,2,3,4,5,6]
157+
158+
iex> stream1 = Stream.cycle([1, 2, 3])
159+
iex> stream2 = Stream.cycle([4, 5, 6])
160+
iex> stream = Stream.concat(stream1, stream2)
161+
iex> Enum.take(stream, 6)
162+
[1,2,3,1,2,3]
163+
164+
"""
165+
@spec concat(Enumerable.t, Enumerable.t) :: t
166+
def concat(first, second) do
167+
&do_concat([first, second], &1, &2)
168+
end
169+
170+
defp do_concat(enumerables, acc, fun) do
171+
Enumerable.reduce(enumerables, acc, &Enumerable.reduce(&1, &2, fun))
172+
end
173+
134174
@doc """
135175
Creates a stream that cycles through the given enumerable,
136176
infinitely.

lib/elixir/test/elixir/enum_test.exs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,27 @@ defmodule EnumTest.List do
5454
assert Enum.at([2, 4, 6], 4, :none) == :none
5555
end
5656

57+
test :concat_1 do
58+
assert Enum.concat([[1, [2], 3], [4], [5, 6]]) == [1, [2], 3, 4, 5, 6]
59+
assert Enum.concat(1..3, []) == [1,2,3]
60+
61+
assert Enum.concat([[], []]) == []
62+
assert Enum.concat([[]]) == []
63+
assert Enum.concat([]) == []
64+
65+
assert Enum.concat([1..5, fn acc, _ -> acc end, [1]]) == [1,2,3,4,5,1]
66+
end
67+
68+
test :concat_2 do
69+
assert Enum.concat([], [1]) == [1]
70+
assert Enum.concat([1, [2], 3], [4, 5]) == [1, [2], 3, 4, 5]
71+
assert Enum.concat(1..3, []) == [1,2,3]
72+
73+
assert Enum.concat([], []) == []
74+
75+
assert Enum.concat(fn acc, _ -> acc end, [1]) == [1]
76+
end
77+
5778
test :fetch! do
5879
assert Enum.fetch!([2, 4, 6], 0) == 2
5980
assert Enum.fetch!([2, 4, 6], 2) == 6

lib/elixir/test/elixir/list_test.exs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,6 @@ defmodule ListTest do
5353
assert List.foldr([1, 2, 3, 4], 0, fn x, y -> x - y end) == -2
5454
end
5555

56-
test :concat_1 do
57-
assert List.concat([[1, [2], 3], [4], [5, 6]]) == [1, [2], 3, 4, 5, 6]
58-
end
59-
60-
test :concat_2 do
61-
assert List.concat([1, [2], 3], [4, 5]) == [1, [2], 3, 4, 5]
62-
end
63-
6456
test :reverse do
6557
assert Enum.reverse([1, 2, 3]) == [3, 2, 1]
6658
end

lib/elixir/test/elixir/stream_test.exs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,37 @@ defmodule StreamTest do
2525
assert Enum.to_list(stream) == [3,5,7]
2626
end
2727

28+
test :concat_1 do
29+
stream = Stream.concat([1..3, [], [4, 5, 6], [], 7..9])
30+
assert is_function(stream)
31+
32+
assert Enum.to_list(stream) == [1,2,3,4,5,6,7,8,9]
33+
assert Enum.take(stream, 5) == [1,2,3,4,5]
34+
35+
stream = Stream.concat([1..3, [4, 5, 6], Stream.cycle(7..100)])
36+
assert is_function(stream)
37+
38+
assert Enum.take(stream, 13) == [1,2,3,4,5,6,7,8,9,10,11,12,13]
39+
end
40+
41+
test :concat_2 do
42+
stream = Stream.concat(1..3, 4..6)
43+
assert is_function(stream)
44+
assert Stream.cycle(stream) |> Enum.take(16) == [1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4]
45+
46+
stream = Stream.concat(1..3, [])
47+
assert is_function(stream)
48+
assert Stream.cycle(stream) |> Enum.take(5) == [1,2,3,1,2]
49+
50+
stream = Stream.concat(1..6, Stream.cycle(7..9))
51+
assert is_function(stream)
52+
assert Stream.drop(stream, 3) |> Enum.take(13) == [4,5,6,7,8,9,7,8,9,7,8,9,7]
53+
54+
stream = Stream.concat(Stream.cycle(1..3), Stream.cycle(4..6))
55+
assert is_function(stream)
56+
assert Enum.take(stream, 13) == [1,2,3,1,2,3,1,2,3,1,2,3,1]
57+
end
58+
2859
test :cycle do
2960
stream = Stream.cycle([1,2,3])
3061
assert is_function(stream)

lib/ex_unit/lib/ex_unit/doc_test.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ defmodule ExUnit.DocTest do
311311

312312
docs = lc doc inlist module.__info__(:docs) do
313313
extract_from_doc(doc)
314-
end |> List.concat
314+
end |> Enum.concat
315315

316316
moduledocs ++ docs
317317
end

lib/iex/lib/iex/introspection.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ defmodule IEx.Introspection do
254254
defp beam_specs(module) do
255255
specs = Enum.map(Kernel.Typespec.beam_specs(module), {:spec, &1})
256256
callbacks = Enum.map(Kernel.Typespec.beam_callbacks(module), {:callback, &1})
257-
List.concat(specs, callbacks)
257+
Enum.concat(specs, callbacks)
258258
end
259259

260260
defp print_type({ kind, type }) do

lib/mix/lib/mix/deps.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ defmodule Mix.Deps do
322322
323323
[ opts[:dest] | sub_dirs ]
324324
|> Enum.map(Path.wildcard(&1))
325-
|> List.concat
325+
|> Enum.concat
326326
|> Enum.map(Path.join(&1, "ebin"))
327327
|> Enum.filter(File.dir?(&1))
328328
end

0 commit comments

Comments
 (0)