forked from exercism/elixir
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
330 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
defmodule LinkedList do | ||
@opaque t :: tuple() | ||
|
||
@doc """ | ||
Construct a new LinkedList | ||
""" | ||
@spec new() :: t | ||
def new() do | ||
{} | ||
end | ||
|
||
@doc """ | ||
Push an item onto a LinkedList | ||
""" | ||
@spec push(t, any()) :: t | ||
def push(list, elem) do | ||
{elem, list} | ||
end | ||
|
||
@doc """ | ||
Calculate the length of a LinkedList | ||
""" | ||
@spec length(t) :: non_neg_integer() | ||
def length(list) do | ||
count_length(list, 0) | ||
end | ||
|
||
defp count_length({}, n), do: n | ||
defp count_length({_, t}, n), do: count_length(t, n + 1) | ||
|
||
@doc """ | ||
Determine if a LinkedList is empty | ||
""" | ||
@spec empty?(t) :: boolean() | ||
def empty?({}), do: true | ||
def empty?(_), do: false | ||
|
||
@doc """ | ||
Get the value of a head of the LinkedList | ||
""" | ||
@spec peek(t) :: {:ok, any()} | {:error, :empty_list} | ||
def peek({}), do: {:error, :empty_list} | ||
def peek({x, _}), do: {:ok, x} | ||
|
||
@doc """ | ||
Get tail of a LinkedList | ||
""" | ||
@spec tail(t) :: {:ok, t} | {:error, :empty_list} | ||
def tail({}), do: {:error, :empty_list} | ||
def tail({_, t}), do: {:ok, t} | ||
|
||
@doc """ | ||
Remove the head from a LinkedList | ||
""" | ||
@spec pop(t) :: {:ok, any(), t} | {:error, :empty_list} | ||
def pop({}), do: {:error, :empty_list} | ||
def pop({h, t}), do: {:ok, h, t} | ||
|
||
@doc """ | ||
Construct a LinkedList from a stdlib List | ||
""" | ||
@spec from_list(list()) :: t | ||
def from_list(list) do | ||
List.foldr(list, new(), &push(&2, &1)) | ||
end | ||
|
||
@doc """ | ||
Construct a stdlib List LinkedList from a LinkedList | ||
""" | ||
@spec to_list(t) :: list() | ||
def to_list(list) do | ||
list |> do_to_list([]) |> Enum.reverse() | ||
end | ||
defp do_to_list({}, acc), do: acc | ||
defp do_to_list({h, t}, acc), do: do_to_list(t, [h|acc]) | ||
|
||
@doc """ | ||
Reverse a LinkedList | ||
""" | ||
@spec reverse(t) :: t | ||
def reverse(list) do | ||
do_reverse(list, new()) | ||
end | ||
def do_reverse({}, acc), do: acc | ||
def do_reverse({h, t}, acc), do: do_reverse(t, push(acc, h)) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
defmodule LinkedList do | ||
@opaque t :: tuple() | ||
|
||
@doc """ | ||
Construct a new LinkedList | ||
""" | ||
@spec new() :: t | ||
def new() do | ||
# Your implementation here... | ||
end | ||
|
||
@doc """ | ||
Push an item onto a LinkedList | ||
""" | ||
@spec push(t, any()) :: t | ||
def push(list, elem) do | ||
# Your implementation here... | ||
end | ||
|
||
@doc """ | ||
Calculate the length of a LinkedList | ||
""" | ||
@spec length(t) :: non_neg_integer() | ||
def length(list) do | ||
# Your implementation here... | ||
end | ||
|
||
@doc """ | ||
Determine if a LinkedList is empty | ||
""" | ||
@spec empty?(t) :: boolean() | ||
def empty?(list) do | ||
# Your implementation here... | ||
end | ||
|
||
@doc """ | ||
Get the value of a head of the LinkedList | ||
""" | ||
@spec peek(t) :: {:ok, any()} | {:error, :empty_list} | ||
def peek(list) do | ||
# Your implementation here... | ||
end | ||
|
||
@doc """ | ||
Get tail of a LinkedList | ||
""" | ||
@spec tail(t) :: {:ok, t} | {:error, :empty_list} | ||
def tail(list) do | ||
# Your implementation here... | ||
end | ||
|
||
@doc """ | ||
Remove the head from a LinkedList | ||
""" | ||
@spec pop(t) :: {:ok, any(), t} | {:error, :empty_list} | ||
def pop(list) do | ||
# Your implementation here... | ||
end | ||
|
||
@doc """ | ||
Construct a LinkedList from a stdlib List | ||
""" | ||
@spec from_list(list()) :: t | ||
def from_list(list) do | ||
# Your implementation here... | ||
end | ||
|
||
@doc """ | ||
Construct a stdlib List LinkedList from a LinkedList | ||
""" | ||
@spec to_list(t) :: list() | ||
def to_list(list) do | ||
# Your implementation here... | ||
end | ||
|
||
@doc """ | ||
Reverse a LinkedList | ||
""" | ||
@spec reverse(t) :: t | ||
def reverse(list) do | ||
# Your implementation here... | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
if !System.get_env("EXERCISM_TEST_EXAMPLES") do | ||
Code.load_file("linked_list.exs", __DIR__) | ||
end | ||
|
||
ExUnit.start | ||
ExUnit.configure exclude: :pending, trace: true | ||
|
||
defmodule LinkedListTest do | ||
use ExUnit.Case | ||
|
||
test "length/1 of new list" do | ||
list = LinkedList.new() | ||
assert LinkedList.length(list) == 0 | ||
end | ||
|
||
@tag :pending | ||
test "empty?/1 of new list" do | ||
list = LinkedList.new() | ||
assert LinkedList.empty?(list) | ||
end | ||
|
||
@tag :pending | ||
test "length/1 of list of 1 datum" do | ||
list = LinkedList.new() |> LinkedList.push(10) | ||
assert LinkedList.length(list) == 1 | ||
end | ||
|
||
@tag :pending | ||
test "empty?/1 of list of 1 datum" do | ||
list = LinkedList.new() |> LinkedList.push(20) | ||
refute LinkedList.empty?(list) | ||
end | ||
|
||
@tag :pending | ||
test "peek/1 of list of 1 datum" do | ||
list = LinkedList.new() |> LinkedList.push(20) | ||
assert LinkedList.peek(list) == {:ok, 20} | ||
end | ||
|
||
@tag :pending | ||
test "peek/1 of list of empty list" do | ||
list = LinkedList.new() | ||
assert LinkedList.peek(list) == {:error, :empty_list} | ||
end | ||
|
||
@tag :pending | ||
test "tail/1 of empty list" do | ||
list = LinkedList.new() | ||
assert {:error, :empty_list} = LinkedList.tail(list) | ||
end | ||
|
||
@tag :pending | ||
test "tail/1 of list of 1 datum" do | ||
list = LinkedList.new() |> LinkedList.push(:hello) | ||
assert {:ok, tail} = LinkedList.tail(list) | ||
assert LinkedList.peek(tail) == {:error, :empty_list} | ||
end | ||
|
||
@tag :pending | ||
test "pushed items are stacked" do | ||
list = | ||
LinkedList.new() | ||
|> LinkedList.push(:a) | ||
|> LinkedList.push(:b) | ||
assert LinkedList.peek(list) == {:ok, :b} | ||
assert {:ok, list} = LinkedList.tail(list) | ||
assert LinkedList.peek(list) == {:ok, :a} | ||
assert {:ok, list} = LinkedList.tail(list) | ||
assert LinkedList.peek(list) == {:error, :empty_list} | ||
end | ||
|
||
@tag :pending | ||
test "push 10 times" do | ||
list = Enum.reduce(1..10, LinkedList.new(), &LinkedList.push(&2, &1)) | ||
assert LinkedList.peek(list) == {:ok, 10} | ||
assert LinkedList.length(list) == 10 | ||
end | ||
|
||
@tag :pending | ||
test "pop/1 of list of 1 datum" do | ||
list = LinkedList.new() |> LinkedList.push(:a) | ||
assert {:ok, :a, tail} = LinkedList.pop(list) | ||
assert LinkedList.length(tail) == 0 | ||
end | ||
|
||
@tag :pending | ||
test "popping frenzy" do | ||
list = Enum.reduce(11..20, LinkedList.new(), &LinkedList.push(&2, &1)) | ||
assert LinkedList.length(list) == 10 | ||
assert {:ok, 20, list} = LinkedList.pop(list) | ||
assert {:ok, 19, list} = LinkedList.pop(list) | ||
assert {:ok, 18, list} = LinkedList.pop(list) | ||
assert {:ok, 17, list} = LinkedList.pop(list) | ||
assert {:ok, 16, list} = LinkedList.pop(list) | ||
assert {:ok, 15} = LinkedList.peek(list) | ||
assert LinkedList.length(list) == 5 | ||
end | ||
|
||
@tag :pending | ||
test "from_list/1 of empty list" do | ||
list = LinkedList.from_list([]) | ||
assert LinkedList.length(list) == 0 | ||
end | ||
|
||
@tag :pending | ||
test "from_list/1 of 2 element list" do | ||
list = LinkedList.from_list([:a, :b]) | ||
assert LinkedList.length(list) == 2 | ||
assert {:ok, :a, list} = LinkedList.pop(list) | ||
assert {:ok, :b, list} = LinkedList.pop(list) | ||
assert {:error, :empty_list} = LinkedList.pop(list) | ||
end | ||
|
||
@tag :pending | ||
test "to_list/1 of empty list" do | ||
list = LinkedList.new() | ||
assert LinkedList.to_list(list) == [] | ||
end | ||
|
||
@tag :pending | ||
test "to_list/1 of list of 1 datum" do | ||
list = LinkedList.from_list([:mon]) | ||
assert LinkedList.to_list(list) == [:mon] | ||
end | ||
|
||
@tag :pending | ||
test "to_list/1 of list of 2 datum" do | ||
list = LinkedList.from_list([:mon, :tues]) | ||
assert LinkedList.to_list(list) == [:mon, :tues] | ||
end | ||
|
||
@tag :pending | ||
test "reverse/1 of list of 2 datum" do | ||
list = LinkedList.from_list([1, 2, 3]) |> LinkedList.reverse() | ||
assert LinkedList.to_list(list) == [3, 2, 1] | ||
end | ||
|
||
@tag :pending | ||
test "reverse/1 of list of 200 datum" do | ||
list = Enum.to_list(1..200) | ||
linked_list = LinkedList.from_list(list) |> LinkedList.reverse() | ||
assert LinkedList.to_list(linked_list) == Enum.reverse(list) | ||
end | ||
|
||
@tag :pending | ||
test "reverse/1 round trip" do | ||
list = Enum.to_list(1..200) | ||
linked_list = | ||
LinkedList.from_list(list) | ||
|> LinkedList.reverse() | ||
|> LinkedList.reverse() | ||
assert LinkedList.to_list(linked_list) == list | ||
end | ||
end |