Skip to content

Commit

Permalink
Fix issues with Enum protocol for dicts
Browse files Browse the repository at this point in the history
  • Loading branch information
alco committed Apr 26, 2012
1 parent 5e92c8e commit ad8113a
Show file tree
Hide file tree
Showing 5 changed files with 278 additions and 171 deletions.
20 changes: 0 additions & 20 deletions lib/dict/dict.ex
Expand Up @@ -68,23 +68,3 @@ end
defmodule HashDict do
use Dict.Common, :"HashDict.Record"
end

defimpl Enum.Iterator, for: HashDict.Record do
import Enum.Iterator.List, only: [iterate: 1]

def iterator(dict) do
{ iterate(&1), iterate({ to_list(dict), fn(pairs, do: extend(Dict.HashDict.Record.empty(dict), pairs)) }) }
end

def ordered_iterator(_) do
raise ArgumentError, message: "Dict does not support ordering"
end

def to_list(dict), do: Dict.HashDict.Record.to_list(dict)

defp extend(dict, pairs) do
List.foldl pairs, dict, fn(pair, dict) ->
Dict.HashDict.Record.put dict, pair
end
end
end
37 changes: 20 additions & 17 deletions lib/dict/orddict.ex
@@ -1,23 +1,26 @@
# We're assuming that the List contains sorted pairs of key-value entries
defimpl Dict, for: List do
defrecord Orddict.Record, data: nil

defimpl Dict, for: Orddict.Record do
def keys(dict) do
:orddict.fetch_keys dict
:orddict.fetch_keys dict.data
end

def values(dict) do
Enum.map dict, fn({_k, v}) -> v end
:orddict.fold fn(_key, value, acc) ->
[value|acc]
end, [], dict.data
end

def size(dict) do
:orddict.size dict
:orddict.size dict.data
end

def has_key?(dict, key) do
:orddict.is_key key, dict
:orddict.is_key key, dict.data
end

def get(dict, key, default // nil) do
case :orddict.find(key, dict) do
case :orddict.find(key, dict.data) do
match: {:ok, value}
value
match: :error
Expand All @@ -26,42 +29,42 @@ defimpl Dict, for: List do
end

def put(dict, key, value) do
:orddict.store key, value, dict
dict.update_data(:orddict.store key, value, &1)
end

def put(dict, {key, value}) do
:orddict.store key, value, dict
dict.update_data(:orddict.store key, value, &1)
end

def delete(dict, key) do
:orddict.erase key, dict
dict.update_data(:orddict.erase key, &1)
end

def merge(d1, d2) do
:orddict.merge fn(_k, _v1, v2) -> v2 end, d1, d2
d1.update_data(:orddict.merge fn(_k, _v1, v2) -> v2 end, &1, d2.data)
end

def merge(d1, d2, fun) do
:orddict.merge fun, d1, d2
d1.update_data(:orddict.merge fun, &1, d2.data)
end

def update(dict, key, fun) do
:orddict.update key, fun, dict
dict.update_data(:orddict.update key, fun, &1)
end

def update(dict, key, initial, fun) do
:orddict.update key, fun, initial, dict
dict.update_data(:orddict.update key, fun, initial, &1)
end

def empty(_) do
[]
Orddict.Record.new(data: [])
end

def to_list(dict) do
dict
dict.data
end
end

defmodule Orddict do
use Dict.Common, :List
use Dict.Common, :"Orddict.Record"
end
95 changes: 76 additions & 19 deletions lib/enum.ex
Expand Up @@ -5,8 +5,15 @@ defprotocol Enum.Iterator do
def to_list(collection)
end

defprotocol Enum.OrdIterator do
@only [List, Record]

def ordered_iterator(collection)
end

defmodule Enum do
require Enum.Iterator, as: I
require Enum.OrdIterator, as: O

@moduledoc """
Provides a set of algorithms that enumerate over collections according to the
Expand Down Expand Up @@ -107,7 +114,7 @@ defmodule Enum do
"""
def drop(collection, count) do
{ iterator, pointer } = I.ordered_iterator(collection)
{ iterator, pointer } = O.ordered_iterator(collection)
drop(iterator, pointer, count)
end

Expand All @@ -125,7 +132,7 @@ defmodule Enum do
#=> [3,4,5]
"""
def drop_while(collection, fun) do
{ iterator, pointer } = I.ordered_iterator(collection)
{ iterator, pointer } = O.ordered_iterator(collection)
drop_while(iterator, pointer, fun)
end

Expand Down Expand Up @@ -298,7 +305,7 @@ defmodule Enum do
"""
def join(collection, joiner // "") do
{ iterator, pointer } = I.ordered_iterator(collection)
{ iterator, pointer } = O.ordered_iterator(collection)
join(iterator, pointer, joiner)
end

Expand Down Expand Up @@ -452,7 +459,7 @@ defmodule Enum do
"""
def split(collection, count) do
{ iterator, pointer } = I.ordered_iterator(collection)
{ iterator, pointer } = O.ordered_iterator(collection)
split(iterator, pointer, count)
end

Expand All @@ -470,7 +477,7 @@ defmodule Enum do
#=> { [1], [2, 3, 4] }
"""
def split_with(collection, fun) do
{ iterator, pointer } = I.ordered_iterator(collection)
{ iterator, pointer } = O.ordered_iterator(collection)
split_with(iterator, pointer, fun)
end

Expand All @@ -490,7 +497,7 @@ defmodule Enum do
"""
def take(collection, count) do
{ iterator, pointer } = I.ordered_iterator(collection)
{ iterator, pointer } = O.ordered_iterator(collection)
take(iterator, pointer, count)
end

Expand All @@ -509,7 +516,7 @@ defmodule Enum do
"""
def take_while(collection, fun // fn(x, do: x)) do
{ iterator, pointer } = I.ordered_iterator(collection)
{ iterator, pointer } = O.ordered_iterator(collection)
take_while(iterator, pointer, fun)
end

Expand Down Expand Up @@ -592,19 +599,19 @@ defmodule Enum do

## drop_while

defp do_drop_while({ h, next }, iterator, fun) do
defp do_drop_while({ h, { t, ctor } = next }, iterator, fun) do
case fun.(h) do
match: false
[h|map(iterator, iterator.(next), fn(x) -> x end)]
ctor.([h|t])
match: nil
[h|map(iterator, iterator.(next), fn(x) -> x end)]
ctor.([h|t])
else:
do_drop_while(iterator.(next), iterator, fun)
end
end

defp do_drop_while({ :stop, _, _ }, _, _) do
[]
defp do_drop_while({ :stop, ctor, _ }, _, _) do
ctor.([])
end

## find
Expand Down Expand Up @@ -698,19 +705,19 @@ defmodule Enum do

## split_with

defp do_split_with({ h, next }, iterator, fun, acc) do
defp do_split_with({ h, { _, ctor } = next }, iterator, fun, acc) do
case fun.(h) do
match: false
do_split_with(iterator.(next), iterator, fun, [h|acc])
match: nil
do_split_with(iterator.(next), iterator, fun, [h|acc])
else:
{ List.reverse(acc), map(iterator, { h, next }, fn(x) -> x end) }
{ ctor.(List.reverse(acc)), map(iterator, { h, next }, fn(x) -> x end) }
end
end

defp do_split_with({ :stop, _, _ }, _, _, acc) do
{ List.reverse(acc), [] }
defp do_split_with({ :stop, ctor, _ }, _, _, acc) do
{ ctor.(List.reverse(acc)), ctor.([]) }
end

## join
Expand Down Expand Up @@ -804,12 +811,12 @@ defmodule Enum do

## take_while

defp do_take_while({ h, next }, iterator, fun, acc) do
defp do_take_while({ h, {_, ctor} = next }, iterator, fun, acc) do
case fun.(h) do
match: false
List.reverse acc
ctor.(List.reverse(acc))
match: nil
List.reverse acc
ctor.(List.reverse(acc))
else:
do_take_while(iterator.(next), iterator, fun, [h|acc])
end
Expand Down Expand Up @@ -869,3 +876,53 @@ defimpl Enum.Iterator, for: List do
{ :stop, ctor, nil}
end
end

defimpl Enum.OrdIterator, for: List do
def ordered_iterator(list) do
Enum.Iterator.List.iterator(list)
end
end

defimpl Enum.Iterator, for: HashDict.Record do
import Enum.Iterator.List, only: [iterate: 1]

def iterator(dict) do
{ iterate(&1), iterate({ to_list(dict), fn(pairs, do: extend(Dict.empty(dict), pairs)) }) }
end

def to_list(dict), do: Dict.to_list(dict)

defp extend(dict, pairs) do
List.foldl pairs, dict, fn(pair, dict) ->
Dict.put dict, pair
end
end
end

defimpl Enum.Iterator, for: Orddict.Record do
import Enum.Iterator.List, only: [iterate: 1]

def iterator(dict) do
{ iterate(&1), iterate({ to_list(dict), fn(pairs, do: extend(Dict.empty(dict), pairs)) }) }
end

def to_list(dict), do: Dict.to_list(dict)

defp extend(dict, pairs) do
List.foldl pairs, dict, fn(pair, dict) ->
Dict.put dict, pair
end
end
end

defimpl Enum.OrdIterator, for: HashDict.Record do
def ordered_iterator(_) do
raise ArgumentError, message: "HashDict does not support ordering"
end
end

defimpl Enum.OrdIterator, for: Orddict.Record do
def ordered_iterator(dict) do
Enum.Iterator.iterator(dict)
end
end
4 changes: 2 additions & 2 deletions test/elixir/dict_test.exs
Expand Up @@ -133,7 +133,7 @@ defmodule DictTest do
DictTest.Common.__using__(HashDict)

test :new do
assert_equal :dict.new, (HashDict.new).data
assert_equal :dict.new, HashDict.new.data
end
end

Expand All @@ -142,6 +142,6 @@ defmodule OrddictTest do
DictTest.Common.__using__(Orddict)

test :new do
assert_equal [], Orddict.new
assert_equal [], Orddict.new.data
end
end

0 comments on commit ad8113a

Please sign in to comment.