From b23c544f8a440818bf44c317c3895d5009ca9e29 Mon Sep 17 00:00:00 2001 From: David Devlin Date: Thu, 8 Dec 2016 21:13:46 +0100 Subject: [PATCH 01/10] Added count/1 --- lib/list_ops.ex | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/list_ops.ex b/lib/list_ops.ex index 3010084..bd11c2d 100644 --- a/lib/list_ops.ex +++ b/lib/list_ops.ex @@ -1,2 +1,6 @@ defmodule ListOps do + def count(l), do: count(l, 0) + + defp count([], acc), do: acc + defp count([h|t], acc), do: count(t, acc+1) end From f3d4d6941519bcc9d64a7eb3d037069bd3b1d67b Mon Sep 17 00:00:00 2001 From: David Devlin Date: Thu, 8 Dec 2016 21:27:31 +0100 Subject: [PATCH 02/10] Added reverse/1 --- lib/list_ops.ex | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/list_ops.ex b/lib/list_ops.ex index bd11c2d..faaccbb 100644 --- a/lib/list_ops.ex +++ b/lib/list_ops.ex @@ -3,4 +3,9 @@ defmodule ListOps do defp count([], acc), do: acc defp count([h|t], acc), do: count(t, acc+1) + + def reverse(l), do: reverse(l, []) + + defp reverse([], acc), do: acc + defp reverse([h|t], acc), do: reverse(t, [h] ++ acc) end From 08160269a6b6b04d45278af48833af8ff90c9681 Mon Sep 17 00:00:00 2001 From: David Devlin Date: Thu, 8 Dec 2016 21:33:36 +0100 Subject: [PATCH 03/10] Fix warning on count/1 --- lib/list_ops.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/list_ops.ex b/lib/list_ops.ex index faaccbb..348149e 100644 --- a/lib/list_ops.ex +++ b/lib/list_ops.ex @@ -2,7 +2,7 @@ defmodule ListOps do def count(l), do: count(l, 0) defp count([], acc), do: acc - defp count([h|t], acc), do: count(t, acc+1) + defp count([_|t], acc), do: count(t, acc+1) def reverse(l), do: reverse(l, []) From 6732a1a445d5fc91488e347f3423ce6cf845771c Mon Sep 17 00:00:00 2001 From: David Devlin Date: Thu, 8 Dec 2016 21:48:27 +0100 Subject: [PATCH 04/10] Added map/2 --- lib/list_ops.ex | 7 ++++++- test/list_ops_test.exs | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/list_ops.ex b/lib/list_ops.ex index 348149e..37eaba7 100644 --- a/lib/list_ops.ex +++ b/lib/list_ops.ex @@ -7,5 +7,10 @@ defmodule ListOps do def reverse(l), do: reverse(l, []) defp reverse([], acc), do: acc - defp reverse([h|t], acc), do: reverse(t, [h] ++ acc) + defp reverse([h|t], acc), do: reverse(t, [h|acc]) + + def map(l, f), do: map(l, f, []) + + defp map([], _, acc), do: reverse(acc) + defp map([h|t], f, acc), do: map(t, f, [f.(h)|acc]) end diff --git a/test/list_ops_test.exs b/test/list_ops_test.exs index c16ecd3..e661ab9 100644 --- a/test/list_ops_test.exs +++ b/test/list_ops_test.exs @@ -25,18 +25,18 @@ defmodule ListOpsTest do assert ListOps.reverse(Enum.to_list(1..1_000_000)) == Enum.to_list(1_000_000..1) end - # test "map of empty list" do - # assert ListOps.map([], &(&1+1)) == [] - # end - # - # test "map of normal list" do - # assert ListOps.map([1,3,5,7], &(&1+1)) == [2,4,6,8] - # end - # - # test "map of huge list" do - # assert ListOps.map(Enum.to_list(1..1_000_000), &(&1+1)) == - # Enum.to_list(2..1_000_001) - # end + test "map of empty list" do + assert ListOps.map([], &(&1+1)) == [] + end + + test "map of normal list" do + assert ListOps.map([1,3,5,7], &(&1+1)) == [2,4,6,8] + end + + test "map of huge list" do + assert ListOps.map(Enum.to_list(1..1_000_000), &(&1+1)) == + Enum.to_list(2..1_000_001) + end # # test "filter of empty list" do # assert ListOps.filter([], &odd?/1) == [] From 5e1baa2139c7e87fdb286e55c2d1282c70ea1271 Mon Sep 17 00:00:00 2001 From: David Devlin Date: Fri, 9 Dec 2016 20:24:43 +0100 Subject: [PATCH 05/10] Added filter/2 --- lib/list_ops.ex | 11 +++++++++++ test/list_ops_test.exs | 28 +++++++++++++++------------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/lib/list_ops.ex b/lib/list_ops.ex index 37eaba7..34749cb 100644 --- a/lib/list_ops.ex +++ b/lib/list_ops.ex @@ -13,4 +13,15 @@ defmodule ListOps do defp map([], _, acc), do: reverse(acc) defp map([h|t], f, acc), do: map(t, f, [f.(h)|acc]) + + def filter(l, f), do: filter(l, f, []) + + defp filter([], _, acc), do: reverse(acc) + defp filter([h|t], f, acc) do + if f.(h) do + filter(t, f, [h|acc]) + else + filter(t, f, acc) + end + end end diff --git a/test/list_ops_test.exs b/test/list_ops_test.exs index e661ab9..727af31 100644 --- a/test/list_ops_test.exs +++ b/test/list_ops_test.exs @@ -1,6 +1,8 @@ defmodule ListOpsTest do use ExUnit.Case + defp odd?(n), do: rem(n, 2) == 1 + test "count of empty list" do assert ListOps.count([]) == 0 end @@ -37,19 +39,19 @@ defmodule ListOpsTest do assert ListOps.map(Enum.to_list(1..1_000_000), &(&1+1)) == Enum.to_list(2..1_000_001) end - # - # test "filter of empty list" do - # assert ListOps.filter([], &odd?/1) == [] - # end - # - # test "filter of normal list" do - # assert ListOps.filter([1,2,3,4], &odd?/1) == [1,3] - # end - # - # test "filter of huge list" do - # assert ListOps.filter(Enum.to_list(1..1_000_000), &odd?/1) == - # Enum.map(1..500_000, &(&1*2-1)) - # end + + test "filter of empty list" do + assert ListOps.filter([], &odd?/1) == [] + end + + test "filter of normal list" do + assert ListOps.filter([1,2,3,4], &odd?/1) == [1,3] + end + + test "filter of huge list" do + assert ListOps.filter(Enum.to_list(1..1_000_000), &odd?/1) == + Enum.map(1..500_000, &(&1*2-1)) + end # # test "reduce of empty list" do # assert ListOps.reduce([], 0, &(&1+&2)) == 0 From fc0c5e5b637b15075b6d23dc9ad532a658723353 Mon Sep 17 00:00:00 2001 From: David Devlin Date: Fri, 9 Dec 2016 20:28:51 +0100 Subject: [PATCH 06/10] Add reduce/3 --- lib/list_ops.ex | 3 +++ test/list_ops_test.exs | 34 +++++++++++++++++----------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/lib/list_ops.ex b/lib/list_ops.ex index 34749cb..c178f96 100644 --- a/lib/list_ops.ex +++ b/lib/list_ops.ex @@ -24,4 +24,7 @@ defmodule ListOps do filter(t, f, acc) end end + + def reduce([], acc, _), do: acc + def reduce([h|t], acc, f), do: reduce(t, f.(h, acc), f) end diff --git a/test/list_ops_test.exs b/test/list_ops_test.exs index 727af31..985634e 100644 --- a/test/list_ops_test.exs +++ b/test/list_ops_test.exs @@ -52,23 +52,23 @@ defmodule ListOpsTest do assert ListOps.filter(Enum.to_list(1..1_000_000), &odd?/1) == Enum.map(1..500_000, &(&1*2-1)) end - # - # test "reduce of empty list" do - # assert ListOps.reduce([], 0, &(&1+&2)) == 0 - # end - # - # test "reduce of normal list" do - # assert ListOps.reduce([1,2,3,4], -3, &(&1+&2)) == 7 - # end - # - # test "reduce of huge list" do - # assert ListOps.reduce(Enum.to_list(1..1_000_000), 0, &(&1+&2)) == - # Enum.reduce(1..1_000_000, 0, &(&1+&2)) - # end - # - # test "reduce with non-commutative function" do - # assert ListOps.reduce([1,2,3,4], 10, fn x, acc -> acc - x end) == 0 - # end + + test "reduce of empty list" do + assert ListOps.reduce([], 0, &(&1+&2)) == 0 + end + + test "reduce of normal list" do + assert ListOps.reduce([1,2,3,4], -3, &(&1+&2)) == 7 + end + + test "reduce of huge list" do + assert ListOps.reduce(Enum.to_list(1..1_000_000), 0, &(&1+&2)) == + Enum.reduce(1..1_000_000, 0, &(&1+&2)) + end + + test "reduce with non-commutative function" do + assert ListOps.reduce([1,2,3,4], 10, fn x, acc -> acc - x end) == 0 + end # # test "append of empty lists" do # assert ListOps.append([], []) == [] From 807e25180589be75702cc1b89d4f416e84e3589c Mon Sep 17 00:00:00 2001 From: David Devlin Date: Fri, 9 Dec 2016 20:35:12 +0100 Subject: [PATCH 07/10] Added append/2 --- lib/list_ops.ex | 3 +++ test/list_ops_test.exs | 42 +++++++++++++++++++++--------------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/lib/list_ops.ex b/lib/list_ops.ex index c178f96..77d7fcc 100644 --- a/lib/list_ops.ex +++ b/lib/list_ops.ex @@ -27,4 +27,7 @@ defmodule ListOps do def reduce([], acc, _), do: acc def reduce([h|t], acc, f), do: reduce(t, f.(h, acc), f) + + def append(l1, l2), do: reduce(reverse(l1), l2, &([&1|&2])) + end diff --git a/test/list_ops_test.exs b/test/list_ops_test.exs index 985634e..61bc5e9 100644 --- a/test/list_ops_test.exs +++ b/test/list_ops_test.exs @@ -69,27 +69,27 @@ defmodule ListOpsTest do test "reduce with non-commutative function" do assert ListOps.reduce([1,2,3,4], 10, fn x, acc -> acc - x end) == 0 end - # - # test "append of empty lists" do - # assert ListOps.append([], []) == [] - # end - # - # test "append of empty and non-empty list" do - # assert ListOps.append([], [1,2,3,4]) == [1,2,3,4] - # end - # - # test "append of non-empty and empty list" do - # assert ListOps.append([1,2,3,4], []) == [1,2,3,4] - # end - # - # test "append of non-empty lists" do - # assert ListOps.append([1,2,3], [4,5]) == [1,2,3,4,5] - # end - # - # test "append of huge lists" do - # assert ListOps.append(Enum.to_list(1..1_000_000), Enum.to_list(1_000_001..2_000_000)) == - # Enum.to_list(1..2_000_000) - # end + + test "append of empty lists" do + assert ListOps.append([], []) == [] + end + + test "append of empty and non-empty list" do + assert ListOps.append([], [1,2,3,4]) == [1,2,3,4] + end + + test "append of non-empty and empty list" do + assert ListOps.append([1,2,3,4], []) == [1,2,3,4] + end + + test "append of non-empty lists" do + assert ListOps.append([1,2,3], [4,5]) == [1,2,3,4,5] + end + + test "append of huge lists" do + assert ListOps.append(Enum.to_list(1..1_000_000), Enum.to_list(1_000_001..2_000_000)) == + Enum.to_list(1..2_000_000) + end # # test "concat of empty list of lists" do # assert ListOps.concat([]) == [] From 5ae39040b7ad126cc5185fbf2f687e0a654fbd4b Mon Sep 17 00:00:00 2001 From: David Devlin Date: Fri, 9 Dec 2016 20:44:15 +0100 Subject: [PATCH 08/10] Added concat/1 --- lib/list_ops.ex | 1 + test/list_ops_test.exs | 36 ++++++++++++++++++------------------ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/list_ops.ex b/lib/list_ops.ex index 77d7fcc..019fc06 100644 --- a/lib/list_ops.ex +++ b/lib/list_ops.ex @@ -30,4 +30,5 @@ defmodule ListOps do def append(l1, l2), do: reduce(reverse(l1), l2, &([&1|&2])) + def concat(l), do: reduce(reverse(l), [], &append/2) end diff --git a/test/list_ops_test.exs b/test/list_ops_test.exs index 61bc5e9..5b83e96 100644 --- a/test/list_ops_test.exs +++ b/test/list_ops_test.exs @@ -90,22 +90,22 @@ defmodule ListOpsTest do assert ListOps.append(Enum.to_list(1..1_000_000), Enum.to_list(1_000_001..2_000_000)) == Enum.to_list(1..2_000_000) end - # - # test "concat of empty list of lists" do - # assert ListOps.concat([]) == [] - # end - # - # test "concat of normal list of lists" do - # assert ListOps.concat([[1,2],[3],[],[4,5,6]]) == [1,2,3,4,5,6] - # end - # - # test "concat of huge list of small lists" do - # assert ListOps.concat(Enum.map(1..1_000_000, &[&1])) == - # Enum.to_list(1..1_000_000) - # end - # - # test "concat of small list of huge lists" do - # assert ListOps.concat(Enum.map(0..9, &Enum.to_list((&1*100_000+1)..((&1+1)*100_000)))) == - # Enum.to_list(1..1_000_000) - # end + + test "concat of empty list of lists" do + assert ListOps.concat([]) == [] + end + + test "concat of normal list of lists" do + assert ListOps.concat([[1,2],[3],[],[4,5,6]]) == [1,2,3,4,5,6] + end + + test "concat of huge list of small lists" do + assert ListOps.concat(Enum.map(1..1_000_000, &[&1])) == + Enum.to_list(1..1_000_000) + end + + test "concat of small list of huge lists" do + assert ListOps.concat(Enum.map(0..9, &Enum.to_list((&1*100_000+1)..((&1+1)*100_000)))) == + Enum.to_list(1..1_000_000) + end end From 056738188a4437b31b1a8dd77b3fbcde626c7f9e Mon Sep 17 00:00:00 2001 From: David Devlin Date: Fri, 9 Dec 2016 21:21:11 +0100 Subject: [PATCH 09/10] Rewrite count/1, reverse/1, map/2 and filter/2 in terms of reduce/3 --- lib/list_ops.ex | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/lib/list_ops.ex b/lib/list_ops.ex index 019fc06..1272f5b 100644 --- a/lib/list_ops.ex +++ b/lib/list_ops.ex @@ -1,29 +1,11 @@ defmodule ListOps do - def count(l), do: count(l, 0) + def count(l), do: reduce(l, 0, fn(_, acc) -> acc + 1 end) - defp count([], acc), do: acc - defp count([_|t], acc), do: count(t, acc+1) + def reverse(l), do: reduce(l, [], &([&1|&2])) - def reverse(l), do: reverse(l, []) + def map(l, f), do: reduce(l, [], &([f.(&1)|&2])) |> reverse - defp reverse([], acc), do: acc - defp reverse([h|t], acc), do: reverse(t, [h|acc]) - - def map(l, f), do: map(l, f, []) - - defp map([], _, acc), do: reverse(acc) - defp map([h|t], f, acc), do: map(t, f, [f.(h)|acc]) - - def filter(l, f), do: filter(l, f, []) - - defp filter([], _, acc), do: reverse(acc) - defp filter([h|t], f, acc) do - if f.(h) do - filter(t, f, [h|acc]) - else - filter(t, f, acc) - end - end + def filter(l, f), do: reduce(l, [], &(if f.(&1) do [&1|&2] else &2 end)) |> reverse def reduce([], acc, _), do: acc def reduce([h|t], acc, f), do: reduce(t, f.(h, acc), f) From c0199dc41f8085418741ee2a2ab2bbc5c2ec1f93 Mon Sep 17 00:00:00 2001 From: David Devlin Date: Fri, 9 Dec 2016 21:33:57 +0100 Subject: [PATCH 10/10] Rewrite append/2 as a special case of concat/1 --- lib/list_ops.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/list_ops.ex b/lib/list_ops.ex index 1272f5b..459d299 100644 --- a/lib/list_ops.ex +++ b/lib/list_ops.ex @@ -10,7 +10,7 @@ defmodule ListOps do def reduce([], acc, _), do: acc def reduce([h|t], acc, f), do: reduce(t, f.(h, acc), f) - def append(l1, l2), do: reduce(reverse(l1), l2, &([&1|&2])) + def append(l1, l2), do: concat([l1, l2]) - def concat(l), do: reduce(reverse(l), [], &append/2) + def concat(l), do: reduce(l, [], fn(i, acc) -> reduce(i, acc, &([&1|&2])) end) |> reverse end