# paulfioravanti's solution

## to List Ops in the Elixir Track

Published at Jul 30 2019 · 0 comments
Instructions
Test suite
Solution

Implement basic list operations.

In functional languages list operations like `length`, `map`, and `reduce` are very common. Implement a series of basic list operations, without using existing functions.

## Running tests

Execute the tests with:

``````\$ mix test
``````

### Pending tests

In the test suites, all but the first test have been skipped.

Once you get a test passing, you can unskip the next one by commenting out the relevant `@tag :pending` with a `#` symbol.

For example:

``````# @tag :pending
test "shouting" do
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end
``````

Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the test suite.

``````# ExUnit.configure exclude: :pending, trace: true
``````

If you're stuck on something, it may help to look at some of the available resources out there where answers might be found.

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

### list_ops_test.exs

``````defmodule ListOpsTest do
alias ListOps, as: L

use ExUnit.Case

defp odd?(n), do: rem(n, 2) == 1

# @tag :pending
test "count of empty list" do
assert L.count([]) == 0
end

@tag :pending
test "count of normal list" do
assert L.count([1, 3, 5, 7]) == 4
end

@tag :pending
test "count of huge list" do
assert L.count(Enum.to_list(1..1_000_000)) == 1_000_000
end

@tag :pending
test "reverse of empty list" do
assert L.reverse([]) == []
end

@tag :pending
test "reverse of normal list" do
assert L.reverse([1, 3, 5, 7]) == [7, 5, 3, 1]
end

@tag :pending
test "reverse of huge list" do
assert L.reverse(Enum.to_list(1..1_000_000)) == Enum.to_list(1_000_000..1)
end

@tag :pending
test "map of empty list" do
assert L.map([], &(&1 + 1)) == []
end

@tag :pending
test "map of normal list" do
assert L.map([1, 3, 5, 7], &(&1 + 1)) == [2, 4, 6, 8]
end

@tag :pending
test "map of huge list" do
assert L.map(Enum.to_list(1..1_000_000), &(&1 + 1)) == Enum.to_list(2..1_000_001)
end

@tag :pending
test "filter of empty list" do
assert L.filter([], &odd?/1) == []
end

@tag :pending
test "filter of normal list" do
assert L.filter([1, 2, 3, 4], &odd?/1) == [1, 3]
end

@tag :pending
test "filter of huge list" do
assert L.filter(Enum.to_list(1..1_000_000), &odd?/1) == Enum.map(1..500_000, &(&1 * 2 - 1))
end

@tag :pending
test "reduce of empty list" do
assert L.reduce([], 0, &(&1 + &2)) == 0
end

@tag :pending
test "reduce of normal list" do
assert L.reduce([1, 2, 3, 4], -3, &(&1 + &2)) == 7
end

@tag :pending
test "reduce of huge list" do
assert L.reduce(Enum.to_list(1..1_000_000), 0, &(&1 + &2)) ==
Enum.reduce(1..1_000_000, 0, &(&1 + &2))
end

@tag :pending
test "reduce with non-commutative function" do
assert L.reduce([1, 2, 3, 4], 10, fn x, acc -> acc - x end) == 0
end

@tag :pending
test "append of empty lists" do
assert L.append([], []) == []
end

@tag :pending
test "append of empty and non-empty list" do
assert L.append([], [1, 2, 3, 4]) == [1, 2, 3, 4]
end

@tag :pending
test "append of non-empty and empty list" do
assert L.append([1, 2, 3, 4], []) == [1, 2, 3, 4]
end

@tag :pending
test "append of non-empty lists" do
assert L.append([1, 2, 3], [4, 5]) == [1, 2, 3, 4, 5]
end

@tag :pending
test "append of huge lists" do
assert L.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

@tag :pending
test "concat of empty list of lists" do
assert L.concat([]) == []
end

@tag :pending
test "concat of normal list of lists" do
assert L.concat([[1, 2], [3], [], [4, 5, 6]]) == [1, 2, 3, 4, 5, 6]
end

@tag :pending
test "concat of huge list of small lists" do
assert L.concat(Enum.map(1..1_000_000, &[&1])) == Enum.to_list(1..1_000_000)
end

@tag :pending
test "concat of small list of huge lists" do
assert L.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``````

### test_helper.exs

``````ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true)``````
``````defmodule ListOps do
# Please don't use any external modules (especially List or Enum) in your
# implementation. The point of this exercise is to create these basic
# functions yourself. You may use basic Kernel functions (like `Kernel.+/2`
# for adding numbers), but please do not use Kernel functions for Lists like
# `++`, `--`, `hd`, `tl`, `in`, and `length`.

@spec count(list) :: non_neg_integer
def count(l), do: count(l, 0)
defp count([], acc), do: acc
defp count([_head | tail], acc), do: count(tail, acc + 1)

@spec reverse(list) :: list
def reverse(l), do: reverse(l, [])
defp reverse([], acc), do: acc

@spec map(list, (any -> any)) :: list
def map(l, f), do: map(l, [], f)
defp map([], acc, _f), do: reverse(acc)
defp map([head | tail], acc, f), do: map(tail, [f.(head) | acc], f)

@spec filter(list, (any -> as_boolean(term))) :: list
def filter(l, f), do: filter(l, [], f)
defp filter([], acc, _f), do: reverse(acc)

defp filter([head | tail], acc, f) do
else
filter(tail, acc, f)
end
end

@type acc :: any
@spec reduce(list, acc, (any, acc -> acc)) :: acc
def reduce([], acc, _f), do: acc

@spec append(list, list) :: list
def append(a, b) do
a
|> reverse()
|> reduce(b, &[&1 | &2])
end

@spec concat([[any]]) :: [any]
def concat(ll) do
ll
|> reverse()
|> reduce([], &append/2)
end
end``````