Skip to content

Instantly share code, notes, and snippets.

@rob-brown
Created November 22, 2016 17:16
Show Gist options
  • Save rob-brown/599c78426e71637e7dc8901bed2aaef5 to your computer and use it in GitHub Desktop.
Save rob-brown/599c78426e71637e7dc8901bed2aaef5 to your computer and use it in GitHub Desktop.
A ZipList inspired from https://youtu.be/IcgmSRJHu_8?t=14m38s
defmodule ZipList do
defstruct previous: [], current: nil, remaining: []
def from_list(list, index \\ 0)
def from_list([], _), do: {:error, :empty_list}
def from_list(list, index) when length(list) < index, do: {:error, :index_out_of_bounds}
def from_list(list, index) when is_list(list) do
previous = list |> Enum.take(index) |> Enum.reverse
[current | remaining] = Enum.drop list, index
ziplist = %__MODULE__{previous: previous, current: current, remaining: remaining}
{:ok, ziplist}
end
def to_list(ziplist) do
tail = [ziplist.current | ziplist.remaining]
Enum.reverse ziplist.previous, tail
end
def previous?(%__MODULE__{previous: previous}), do: previous != []
def remaining?(%__MODULE__{remaining: remaining}), do: remaining != []
def advance(z = %__MODULE__{remaining: []}), do: z
def advance(z = %__MODULE__{remaining: [remaining | rest]}) do
%__MODULE__{previous: [z.current | z.previous], current: remaining, remaining: rest}
end
def retreat(z = %__MODULE__{previous: []}), do: z
def retreat(z = %__MODULE__{previous: [previous | rest]}) do
%__MODULE__{previous: rest, current: previous, remaining: [z.current | z.remaining]}
end
def current_index(ziplist) do
Enum.count(ziplist.previous) + 1
end
end
defimpl Enumerable, for: ZipList do
def count(%ZipList{remaining: remaining, previous: previous}) do
count = Enum.count(remaining) + Enum.count(previous) + 1
{:ok, count}
end
def member?(%ZipList{previous: previous, current: current, remaining: remaining}, value) do
result = current == value or Enum.member?(previous, value) or Enum.member?(remaining, value)
{:ok, result}
end
def reduce(ziplist, acc, fun) do
ziplist |> ZipList.to_list |> Enumerable.List.reduce(acc, fun)
end
end
defimpl Collectable, for: ZipList do
def into(ziplist) do
{{ziplist, []}, fn
{ziplist, values}, {:cont, item} -> {ziplist, [item | values]}
{ziplist, values}, :done -> %ZipList{ziplist | remaining: ziplist.remaining ++ Enum.reverse(values)}
_, :halt -> :ok
end}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment