Current section
Files
Jump to
Current section
Files
lib/dlist.ex
defmodule DiffList do
@moduledoc """
Difference lists are a way of encoding a list as the action of preappending them.
Instead of a list being `[1, 2, 3]`, it is the anonymous function `fn(ys) -> [1, 2, 3] ++ ys end`.
Difference lists are fast for left-associated appends (`list ++ [x]`) as they are represented as function composition.
Refer to [this](http://h2.jaguarpaw.co.uk/posts/demystifying-dlist/) excellent blog post for more information.
"""
alias DiffList.Utils
@type difflist :: (list -> list)
@doc """
Converts a list into a difference list.
"""
@spec from_list(list) :: difflist
def from_list(xs) do
fn ys -> xs ++ ys end
end
@doc """
Convert a difference list into a list.
## Examples
iex> DiffList.from_list([1, 2, 3]) |> DiffList.to_list
[1, 2, 3]
"""
@spec to_list(difflist) :: list
def to_list(difflist) do
difflist.([])
end
@doc """
Returns an empty difference list.
"""
@spec empty() :: difflist
def empty, do: from_list([])
@doc """
Returns a difference list of one item.
## Example
iex> DiffList.singleton(1) |> DiffList.to_list
[1]
"""
@spec singleton(any) :: difflist
def singleton(x) do
cons(empty(), x)
end
@doc """
Append a difference list to another difference list.
## Example
iex> x = DiffList.from_list([1, 2, 3])
iex> y = DiffList.from_list([4, 5, 6])
iex> DiffList.append(x, y) |> DiffList.to_list
[1, 2, 3, 4, 5, 6]
"""
@spec append(difflist, difflist) :: difflist
def append(difflist_a, difflist_b) do
Utils.compose(difflist_a, difflist_b)
end
@doc """
Prepends an item to a difference list.
The difference list equivalent of `[x] ++ list`.
## Example
iex> x = DiffList.from_list([2, 3])
iex> DiffList.cons(x, 1) |> DiffList.to_list
[1, 2, 3]
"""
@spec cons(difflist, any) :: difflist
def cons(difflist, x) do
Utils.compose(Utils.list_cons(x), difflist)
end
@doc """
Appends an item to a difference list.
The difference list equivalent of `list ++ [x]`.
## Example
iex> x = DiffList.from_list([1, 2])
iex> DiffList.snoc(x, 3) |> DiffList.to_list
[1, 2, 3]
"""
@spec snoc(difflist, any) :: difflist
def snoc(difflist, x) do
Utils.compose(difflist, Utils.list_cons(x))
end
@doc """
Gets the first element of a difference list.
Essentially the same as `hd(list)`.
## Example
iex> x = DiffList.from_list([1, 2, 3])
iex> DiffList.head(x)
1
"""
@spec head(difflist) :: any
def head(difflist), do: list(&Utils.const/2, difflist)
@doc """
Gets the tail a difference list.
Essentially the same as `tl(list)`.
## Example
iex> x = DiffList.from_list([1, 2, 3])
iex> DiffList.tail(x) |> DiffList.to_list
[2, 3]
"""
@spec tail(difflist) :: list
def tail(difflist), do: list(&Utils.flipped_const/2, difflist)
@doc """
Concatenates a list of difference lists into one difference list.
## Example
iex> x = DiffList.from_list([1, 2, 3])
iex> y = DiffList.from_list([4, 5, 6])
iex> z = [x, y]
iex> DiffList.concat(z) |> DiffList.to_list
[1, 2, 3, 4, 5, 6]
"""
@spec concat(list(difflist)) :: difflist
def concat(difflists) do
Enum.reduce(difflists, empty(), fn(x, acc) -> append(acc, x) end)
end
defp list(fun, difflist) do
case to_list(difflist) do
[] -> nil
[h | t] -> fun.(h, from_list(t))
end
end
end