Skip to content

Commit

Permalink
Introduce List.Dict, DRYing the tuple list as Dict implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Devin Torres committed Jan 17, 2013
1 parent 4f26d1a commit 644b765
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 179 deletions.
168 changes: 39 additions & 129 deletions lib/elixir/lib/dict.ex
Expand Up @@ -5,12 +5,6 @@ defmodule Dict do
functions that redirect to the underlying Dict based on
the tuple signature.
The keyword list used throughout Elixir cannot be
manipulated via the Dict module, you must use the
Keyword module instead. This distinction is intentional:
the Dict module is meant to work on structures that work
as storage.
To create a new dict, use the `new` functions defined
by each dict type:
Expand Down Expand Up @@ -45,6 +39,17 @@ defmodule Dict do
defcallback update(t, key, (value -> value)) :: t
defcallback values(t) :: list(value)

defmacrop target(dict) do
quote do
cond do
is_tuple(unquote(dict)) ->
elem(unquote(dict), 0)
is_list(unquote(dict)) ->
List.Dict
end
end
end

@doc """
Returns a list containing all dict's keys.
The keys are not guaranteed to be sorted, unless
Expand All @@ -57,12 +62,8 @@ defmodule Dict do
"""
@spec keys(t) :: [key]
def keys(dict) when is_tuple(dict) do
elem(dict, 0).keys(dict)
end

def keys(dict) when is_list(dict) do
lc { key, _ } inlist dict, do: key
def keys(dict) do
target(dict).keys(dict)
end

@doc """
Expand All @@ -75,12 +76,8 @@ defmodule Dict do
"""
@spec values(t) :: [value]
def values(dict) when is_tuple(dict) do
elem(dict, 0).values(dict)
end

def values(dict) when is_list(dict) do
lc { _, value } inlist dict, do: value
def values(dict) do
target(dict).values(dict)
end

@doc """
Expand All @@ -93,12 +90,8 @@ defmodule Dict do
"""
@spec size(t) :: non_neg_integer
def size(dict) when is_tuple(dict) do
elem(dict, 0).size(dict)
end

def size(dict) when is_list(dict) do
length(dict)
def size(dict) do
target(dict).size(dict)
end

@doc """
Expand All @@ -112,12 +105,8 @@ defmodule Dict do
"""
@spec has_key?(t, key) :: boolean
def has_key?(dict, key) when is_tuple(dict) do
elem(dict, 0).has_key?(dict, key)
end

def has_key?(dict, key) when is_list(dict) do
:lists.keymember(key, 1, dict)
def has_key?(dict, key) do
target(dict).has_key?(dict, key)
end

@doc """
Expand All @@ -133,17 +122,8 @@ defmodule Dict do
"""
@spec get(t, key, value) :: value
def get(dict, key, default // nil)

def get(dict, key, default) when is_tuple(dict) do
elem(dict, 0).get(dict, key, default)
end

def get(dict, key, default) when is_list(dict) do
case :lists.keyfind(key, 1, dict) do
{ ^key, value } -> value
false -> default
end
def get(dict, key, default // nil) do
target(dict).get(dict, key, default)
end

@doc """
Expand All @@ -158,15 +138,8 @@ defmodule Dict do
"""
@spec get!(t, key) :: value | no_return
def get!(dict, key) when is_tuple(dict) do
elem(dict, 0).get!(dict, key)
end

def get!(dict, key) when is_list(dict) do
case :lists.keyfind(key, 1, dict) do
{ ^key, value } -> value
false -> raise(KeyError, key: key)
end
def get!(dict, key) do
target(dict).get!(dict, key)
end

@doc """
Expand All @@ -181,12 +154,8 @@ defmodule Dict do
"""
@spec put(t, key, value) :: t
def put(dict, key, val) when is_tuple(dict) do
elem(dict, 0).put(dict, key, val)
end

def put(dict, key, val) when is_list(dict) do
[{key, val}|delete(dict, key)]
def put(dict, key, val) do
target(dict).put(dict, key, val)
end

@doc """
Expand All @@ -200,15 +169,8 @@ defmodule Dict do
"""
@spec put_new(t, key, value) :: t
def put_new(dict, key, val) when is_tuple(dict) do
elem(dict, 0).put_new(dict, key, val)
end

def put_new(dict, key, val) when is_list(dict) do
case :lists.keyfind(key, 1, dict) do
{ ^key, _ } -> dict
false -> [{key,val}|dict]
end
def put_new(dict, key, val) do
target(dict).put_new(dict, key, val)
end

@doc """
Expand All @@ -225,12 +187,8 @@ defmodule Dict do
"""
@spec delete(t, key) :: t
def delete(dict, key) when is_tuple(dict) do
elem(dict, 0).delete(dict, key)
end

def delete(dict, key) when is_list(dict) do
lc { k, _ } = tuple inlist dict, key != k, do: tuple
def delete(dict, key) do
target(dict).delete(dict, key)
end

@doc """
Expand All @@ -248,10 +206,6 @@ defmodule Dict do
"""
@spec merge(t, t) :: t
def merge(dict1, dict2) when is_list(dict1) and is_list(dict2) do
dict2 ++ lc({ k, _ } = tuple inlist dict1, not has_key?(dict2, k), do: tuple)
end

def merge(dict1, dict2) do
merge(dict1, dict2, fn(_k, _v1, v2) -> v2 end)
end
Expand All @@ -271,20 +225,8 @@ defmodule Dict do
"""
@spec merge(t, t, (key, value, value -> value)) :: t
def merge(dict1, dict2, fun) when is_tuple(dict1) do
elem(dict1, 0).merge(dict1, dict2, fun)
end

def merge(dict1, dict2, fun) when is_list(dict1) and is_list(dict2) do
do_merge(dict2, dict1, fun)
end

defp do_merge([{ k, v2 }|t], acc, fun) do
do_merge t, update(acc, k, v2, fn(v1) -> fun.(k, v1, v2) end), fun
end

defp do_merge([], acc, _fun) do
acc
def merge(dict1, dict2, fun) do
target(dict1).merge(dict1, dict2, fun)
end

@doc """
Expand All @@ -299,20 +241,8 @@ defmodule Dict do
"""
@spec update(t, key, (value -> value)) :: t
def update(dict, key, fun) when is_tuple(dict) do
elem(dict, 0).update(dict, key, fun)
end

def update([{key, value}|dict], key, fun) do
[{key, fun.(value)}|delete(dict, key)]
end

def update([{_, _} = e|dict], key, fun) do
[e|update(dict, key, fun)]
end

def update([], key, _fun) do
raise(KeyError, key: key)
def update(dict, key, fun) do
target(dict).update(dict, key, fun)
end

@doc """
Expand All @@ -328,44 +258,24 @@ defmodule Dict do
"""
@spec update(t, key, value, (value -> value)) :: t
def update(dict, key, initial, fun) when is_tuple(dict) do
elem(dict, 0).update(dict, key, initial, fun)
end

def update([{key, value}|dict], key, _initial, fun) do
[{key, fun.(value)}|delete(dict, key)]
end

def update([{_, _} = e|dict], key, initial, fun) do
[e|update(dict, key, initial, fun)]
end

def update([], key, initial, _fun) do
[{key, initial}]
def update(dict, key, initial, fun) do
target(dict).update(dict, key, initial, fun)
end

@doc """
Returns an empty dict of the same type as `dict`.
"""
@spec empty(t) :: t
def empty(dict) when is_tuple(dict) do
elem(dict, 0).empty(dict)
end

def empty(dict) when is_list(dict) do
[]
def empty(dict) do
target(dict).empty(dict)
end

@doc """
Returns a list of key-value pairs stored in `dict`.
No particular order is enforced.
"""
@spec to_list(t) :: list
def to_list(dict) when is_tuple(dict) do
elem(dict, 0).to_list(dict)
end

def to_list(dict) when is_list(dict) do
dict
def to_list(dict) do
target(dict).to_list(dict)
end
end
112 changes: 112 additions & 0 deletions lib/elixir/lib/list/dict.ex
@@ -0,0 +1,112 @@
defmodule List.Dict do
@doc false
def new, do: []

@doc false
def new(pairs) do
Enum.reduce pairs, new, fn { k, v }, acc ->
[ {k, v} | acc ]
end
end

@doc false
def new(list, transform) when is_function(transform) do
Enum.reduce list, [], fn i, acc ->
{ k, v } = transform.(i)
[ {k, v} | acc ]
end
end

@doc false
def keys(dict) do
lc { key, _ } inlist dict, do: key
end

@doc false
def values(dict) do
lc { _, value } inlist dict, do: value
end

@doc false
def size(dict) do
length(dict)
end

@doc false
def has_key?(dict, key) do
:lists.keymember(key, 1, dict)
end

@doc false
def get(dict, key, default) do
case :lists.keyfind(key, 1, dict) do
{ ^key, value } -> value
false -> default
end
end

@doc false
def get!(dict, key) do
case :lists.keyfind(key, 1, dict) do
{ ^key, value } -> value
false -> raise(KeyError, key: key)
end
end

@doc false
def put(dict, key, val) do
[{key, val}|delete(dict, key)]
end

@doc false
def put_new(dict, key, val) do
case :lists.keyfind(key, 1, dict) do
{ ^key, _ } -> dict
false -> [{key,val}|dict]
end
end

@doc false
def delete(dict, key) do
lc { k, _ } = tuple inlist dict, key != k, do: tuple
end

@doc false
def merge(dict1, dict2, fun) do
Enum.reduce dict2, dict1, fn { k, v2 }, acc ->
update(acc, k, v2, fn(v1) -> fun.(k, v1, v2) end)
end
end

@doc false
def update([{key, value}|dict], key, fun) do
[{key, fun.(value)}|delete(dict, key)]
end

def update([{_, _} = e|dict], key, fun) do
[e|update(dict, key, fun)]
end

def update([], key, _fun) do
raise(KeyError, key: key)
end

@doc false
def update([{key, value}|dict], key, _initial, fun) do
[{key, fun.(value)}|delete(dict, key)]
end

def update([{_, _} = e|dict], key, initial, fun) do
[e|update(dict, key, initial, fun)]
end

def update([], key, initial, _fun) do
[{key, initial}]
end

@doc false
def empty(_dict), do: []

@doc false
def to_list(dict), do: dict
end

0 comments on commit 644b765

Please sign in to comment.