Skip to content

Instantly share code, notes, and snippets.

@sasa1977
Created August 7, 2013 22:25
Show Gist options
  • Save sasa1977/6179429 to your computer and use it in GitHub Desktop.
Save sasa1977/6179429 to your computer and use it in GitHub Desktop.
defmodule Dict.Behaviour do
# It is assumed that the client module implements following functions:
# size/1, fetch/2, put/3, dict_delete/2
#
# And that it defines Enumerable implementation
defmacro __using__(_) do
quote do
# Following are exact copies of HashDict:
def get(dict, key, default // nil) do
case fetch(dict, key) do
{ :ok, value } -> value
:error -> default
end
end
def has_key?(dict, key) do
match? { :ok, _ }, fetch(dict, key)
end
def put_new(dict, key, value) do
update(dict, key, value, fn(v) -> v end)
end
def pop(dict, key, default // nil) do
case dict_delete(dict, key) do
{ dict, _, 0 } -> { default, dict }
{ dict, value, _ } -> { value, dict }
end
end
def split(dict, keys) do
split(keys, new, dict)
end
defp split([], including, excluding) do
{ including, excluding }
end
defp split([key|keys], including, excluding) do
case dict_delete(excluding, key) do
{ excluding, _, 0 } -> split(keys, including, excluding)
{ excluding, value, _ } -> split(keys, put(including, key, value), excluding)
end
end
def drop(dict, []), do: dict
def drop(dict, [key|keys]) do
drop(delete(dict, key), keys)
end
def take(dict, keys) do
take(dict, keys, new)
end
defp take(_dict, [], acc), do: acc
defp take(dict, [key|keys], acc) do
case fetch(dict, key) do
{ :ok, value } -> take(dict, keys, put(acc, key, value))
:error -> take(dict, keys, acc)
end
end
def delete(dict, key) do
{ dict, _, _ } = dict_delete(dict, key)
dict
end
# end of exact copies
# Almost the same, but without the guard 'when is_tuple(dict)'
def fetch!(dict, key) do
case fetch(dict, key) do
{ :ok, value } -> value
:error -> raise(KeyError, key: key)
end
end
# Very similar implementation, but relies on Enum
def to_list(dict), do: Enum.to_list(dict)
# Very similar implementation, but relies on Enum
def keys(dict), do: Enum.reduce(dict, [], fn({k, _}, acc) -> [k | acc] end) |> Enum.reverse
# Very similar implementation, but relies on Enum
def values(dict), do: Enum.reduce(dict, [], fn({_, v}, acc) -> [v | acc] end) |> Enum.reverse
# Very similar implementation, but relies on Enum
def equal?(dict1, dict2) do
case __MODULE__.size(dict1) == __MODULE__.size(dict2) do
false -> false
true ->
try do
Enum.each(dict1, fn(element) ->
unless Enumerable.member?(dict2, element), do: throw(:error)
end)
true
catch
:error -> false
end
end
end
# What is the difference between this and fetch! ?
def get!(dict, key) do
fetch!(dict, key)
end
# Different implementation, slower than HashDict
def update(dict, key, fun) do
case fetch(dict, key) do
:error -> dict
{:ok, value} -> put(dict, key, fun.(value))
end
end
# Different implementation, slower than HashDict
def update(dict, key, initial, fun) do
case fetch(dict, key) do
:error -> put(dict, key, fun.(initial))
{:ok, value} -> put(dict, key, fun.(value))
end
end
# Radically different implementation. I'm not sure about the speed.
def merge(dict, enumerable, callback // fn(_k, _v1, v2) -> v2 end) do
Enum.reduce(enumerable, dict, fn({key, value}, dict) ->
new_value = case fetch(dict, key) do
:error -> value
{:ok, old_value} -> callback.(key, old_value, value)
end
put(dict, key, new_value)
end)
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment