Skip to content

Commit 01e8480

Browse files
committed
Implement Enum.equal? with equality callback
1 parent 2e26d17 commit 01e8480

File tree

2 files changed

+185
-9
lines changed

2 files changed

+185
-9
lines changed

lib/elixir/lib/enum.ex

Lines changed: 173 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,18 @@ defmodule Enum do
376376
{ { _, _ }, { _, :stop } } ->
377377
false
378378

379+
{ { _, :stop }, [] } ->
380+
true
381+
382+
{ [], { _, :stop } } ->
383+
true
384+
385+
{ { _, :stop }, _ } ->
386+
false
387+
388+
{ _, { _, :stop } } ->
389+
false
390+
379391
{ { a_iterator, a_pointer }, { b_iterator, b_pointer } } ->
380392
do_equal?(a_pointer, a_iterator, b_pointer, b_iterator)
381393

@@ -390,6 +402,93 @@ defmodule Enum do
390402
end
391403
end
392404

405+
@doc """
406+
Returns true if the first collection is equal to the second, every element in
407+
both collections is iterated through and compared with the passed function,
408+
as soon as an element differs, it returns false.
409+
410+
Please note that the first parameter passed to the compare function isn't
411+
ensured to be an element from the first collection.
412+
413+
## Examples
414+
415+
iex> Enum.equal?([], [], &1 === &2)
416+
true
417+
iex> Enum.equal?([1 .. 3], [[1, 2, 3]], Enum.equal?(&1, &2))
418+
true
419+
iex> Enum.equal?(1 .. 3, [1.0, 2.0, 3.0], &1 === &2)
420+
false
421+
iex> Enum.equal?(1 .. 3, [], &1 === &2)
422+
false
423+
424+
"""
425+
@spec equal?(t, t, ((term, term) -> boolean)) :: boolean
426+
def equal?([], [], _) do
427+
true
428+
end
429+
430+
def equal?(a, b, _) when is_list(a) and is_list(b) and a == [] or b == [] do
431+
false
432+
end
433+
434+
def equal?(a, b, fun) when is_list(a) and is_list(b) do
435+
do_equal_with?(fun, a, b)
436+
end
437+
438+
def equal?(a, b, fun) when is_list(a) do
439+
equal?(b, a, fun)
440+
end
441+
442+
def equal?(a, b, fun) when is_list(b) do
443+
case I.iterator(a) do
444+
{ _, :stop } ->
445+
b == []
446+
447+
{ iterator, pointer } ->
448+
do_equal_with?(fun, pointer, iterator, b)
449+
450+
list ->
451+
do_equal_with?(fun, list, b)
452+
end
453+
end
454+
455+
def equal?(a, b, fun) do
456+
case { I.iterator(a), I.iterator(b) } do
457+
{ { _, :stop }, { _, :stop } } ->
458+
true
459+
460+
{ { _, :stop }, { _, _ } } ->
461+
false
462+
463+
{ { _, _ }, { _, :stop } } ->
464+
false
465+
466+
{ { _, :stop }, [] } ->
467+
true
468+
469+
{ [], { _, :stop } } ->
470+
true
471+
472+
{ { _, :stop }, _ } ->
473+
false
474+
475+
{ _, { _, :stop } } ->
476+
false
477+
478+
{ { a_iterator, a_pointer }, { b_iterator, b_pointer } } ->
479+
do_equal_with?(fun, a_pointer, a_iterator, b_pointer, b_iterator)
480+
481+
{ { iterator, pointer }, b } ->
482+
do_equal_with?(fun, pointer, iterator, b)
483+
484+
{ a, { iterator, pointer } } ->
485+
do_equal_with?(fun, pointer, iterator, a)
486+
487+
{ a, b } ->
488+
do_equal_with?(fun, a, b)
489+
end
490+
end
491+
393492
@doc """
394493
Filters the collection, i.e. returns only those elements
395494
for which `fun` returns true.
@@ -1184,14 +1283,28 @@ defmodule Enum do
11841283

11851284
## equal?
11861285

1187-
defp do_equal?({ a, _ }, _, [b | _]) when a != b do
1286+
# iterator : iterator
1287+
defp do_equal?(:stop, _, :stop, _) do
1288+
true
1289+
end
1290+
1291+
defp do_equal?(:stop, _, _, _) do
11881292
false
11891293
end
11901294

1191-
defp do_equal?({ _, a_next }, iterator, [_ | b_next]) do
1192-
do_equal?(iterator.(a_next), iterator, b_next)
1295+
defp do_equal?(_, _, :stop, _) do
1296+
false
1297+
end
1298+
1299+
defp do_equal?({ a, _ }, _, { b, _ }, _) when a != b do
1300+
false
11931301
end
11941302

1303+
defp do_equal?({ _, a_next }, a_iterator, { _, b_next }, b_iterator) do
1304+
do_equal?(a_iterator.(a_next), a_iterator, b_iterator.(b_next), b_iterator)
1305+
end
1306+
1307+
# iterator : list
11951308
defp do_equal?({ _, _ }, _, []) do
11961309
false
11971310
end
@@ -1204,26 +1317,77 @@ defmodule Enum do
12041317
false
12051318
end
12061319

1207-
defp do_equal?({ a, _a_next }, _a_iterator, { b, _b_next }, _b_iterator) when a != b do
1320+
defp do_equal?({ a, _ }, _, [b | _]) when a != b do
12081321
false
12091322
end
12101323

1211-
defp do_equal?({ _a, a_next }, a_iterator, { _b, b_next }, b_iterator) do
1212-
do_equal?(a_iterator.(a_next), a_iterator, b_iterator.(b_next), b_iterator)
1324+
defp do_equal?({ _, a_next }, iterator, [_ | b_next]) do
1325+
do_equal?(iterator.(a_next), iterator, b_next)
12131326
end
12141327

1215-
defp do_equal?(:stop, _a_iterator, :stop, _b_iterator) do
1328+
# iterator : iterator
1329+
defp do_equal_with?(_, :stop, _, :stop, _) do
12161330
true
12171331
end
12181332

1219-
defp do_equal?(:stop, _a_iterator, _b_pointer, _b_iterator) do
1333+
defp do_equal_with?(_, :stop, _, _, _) do
12201334
false
12211335
end
12221336

1223-
defp do_equal?(_a_pointer, _a_iterator, :stop, _b_iterator) do
1337+
defp do_equal_with?(_, _, _, :stop, _) do
12241338
false
12251339
end
12261340

1341+
defp do_equal_with?(fun, { a, a_next }, a_iterator, { b, b_next }, b_iterator) do
1342+
if fun.(a, b) do
1343+
do_equal_with?(fun, a_iterator.(a_next), a_iterator, b_iterator.(b_next), b_iterator)
1344+
else
1345+
false
1346+
end
1347+
end
1348+
1349+
# iterator : list
1350+
defp do_equal_with?(_, :stop, _, []) do
1351+
true
1352+
end
1353+
1354+
defp do_equal_with?(_, :stop, _, _) do
1355+
false
1356+
end
1357+
1358+
defp do_equal_with?(_, { _, _ }, _, []) do
1359+
false
1360+
end
1361+
1362+
defp do_equal_with?(fun, { a, a_next }, iterator, [b | b_next]) do
1363+
if fun.(a, b) do
1364+
do_equal_with?(fun, iterator.(a_next), iterator, b_next)
1365+
else
1366+
false
1367+
end
1368+
end
1369+
1370+
# list : list
1371+
defp do_equal_with?(_, [], []) do
1372+
true
1373+
end
1374+
1375+
defp do_equal_with?(_, [], _) do
1376+
false
1377+
end
1378+
1379+
defp do_equal_with?(_, _, []) do
1380+
false
1381+
end
1382+
1383+
defp do_equal_with?(fun, [a | a_next], [b | b_next]) do
1384+
if fun.(a, b) do
1385+
do_equal_with?(fun, a_next, b_next)
1386+
else
1387+
false
1388+
end
1389+
end
1390+
12271391
## find
12281392

12291393
defp do_find([h|t], ifnone, fun) do

lib/elixir/test/elixir/enum_test.exs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,18 @@ defmodule EnumTest.List do
8888
assert Enum.equal?(1 .. 3, 1 .. 3)
8989
refute Enum.equal?(1 .. 3, 1 .. 10)
9090
refute Enum.equal?(1 .. 3, [])
91+
assert Enum.equal?([1, 2, 3], 1 .. 3)
92+
assert Enum.equal?(1 .. 3, 1 .. 3)
93+
refute Enum.equal?(1 .. 3, 1 .. 10)
94+
refute Enum.equal?(1 .. 3, [])
95+
refute Enum.equal?([], 1 .. 3)
96+
97+
refute Enum.equal?(1 .. 3, [1.0, 2.0, 3.0], &1 === &2)
98+
refute Enum.equal?(1 .. 3, [], &1 === &2)
99+
refute Enum.equal?([], 1 .. 3, &1 === &2)
100+
refute Enum.equal?(1 .. 3, 1 .. 5, &1 == &2)
101+
assert Enum.equal?(1 .. 3, [1, 2, 3], &1 === &2)
102+
assert Enum.equal?([1, 2, 3], [1, 2, 3], &1 === &2)
91103
end
92104

93105
test :each do

0 commit comments

Comments
 (0)