diff --git a/lib/array.ex b/lib/array.ex index 4fc9639..cd89853 100644 --- a/lib/array.ex +++ b/lib/array.ex @@ -15,7 +15,7 @@ defmodule Array do Creates a new, extendible array with initial size zero. The default value is the atom nil, not undefined. """ - @spec new() :: t + @spec new() :: t def new() do %Array{content: :array.new({:default, nil})} end @@ -85,7 +85,7 @@ defmodule Array do @doc """ Folds the elements of the array using the given function and initial accumulator value. - The elements are visited in order from the lowest index to the highest. + The elements are visited in order from the lowest index to the highest. If `fun` is not a function, the call raises `ArgumentError`. """ @@ -96,7 +96,7 @@ defmodule Array do @doc """ Folds the elements of the array right-to-left using the given function and initial accumulator value. The elements are visited in order from the highest index to the lowest. - + If `fun` is not a function, the call raises `ArgumentError`. """ @spec foldr(t, acc, (index, element, acc -> acc)) :: acc when acc: var @@ -155,11 +155,36 @@ defmodule Array do @doc """ Gets the value of entry `idx`. If `idx` is not a nonnegative integer, or if the array has - fixed size and `idx` is larger than the maximum index, the call raises `ArgumentError`. + fixed size and `idx` is larger than the maximum index, the call returns :error """ - @spec get(t, index) :: element - def get(%Array{content: c}, idx), - do: :array.get(idx, c) + @spec fetch(t, index) :: element + def fetch(%Array{content: c}, idx) do + cond do + :array.size(c) == 0 -> :error + idx in 0..(:array.size(c) - 1) -> {:ok, :array.get(idx, c)} + true -> :error + end + end + + @doc """ + Gets the value of entry `idx`. If `idx` is not a nonnegative integer, or if the array has + fixed size and `idx` is larger than the maximum index it returns `default` or array's default value. + """ + @spec get(t, index, any) :: element + def get(array, idx, default \\ nil) do + case Array.fetch(array, idx) do + {:ok, value} -> value + :error -> default || Array.default(array) + end + end + + @doc """ + Gets and updates the container’s value for the given key, in a single pass. + """ + def get_and_update(arr, idx, fun) do + {get, update} = fun.(Array.get(arr, idx)) + {get, Array.set(arr, idx, update)} + end @doc """ Returns `true` if `arr` appears to be an array, otherwise `false`. @@ -184,7 +209,7 @@ defmodule Array do @doc """ Maps the given function onto each element of the array. The elements are visited in order from the lowest index to the highest. - + If `fun` is not a function, the call raises `ArgumentError`. """ @spec map(t, (index, element -> any)) :: t @@ -260,7 +285,7 @@ defmodule Array do Folds the elements of the array right-to-left using the given function and initial accumulator value, skipping default-valued entries. The elements are visited in order from the highest index to the lowest. - + If `fun` is not a function, the call raises `ArgumentError`. """ @spec sparse_foldr(t, acc, (index, element, acc -> acc)) :: acc when acc: var @@ -270,7 +295,7 @@ defmodule Array do @doc """ Maps the given function onto each element of the array, skipping default-valued entries. The elements are visited in order from the lowest index to the highest. - + If `fun` is not a function, the call raises `ArgumentError`. """ @spec sparse_map(t, (element -> any)) :: t @@ -322,17 +347,6 @@ defmodule Array do do: :array.to_orddict(c) end -defimpl Access, for: Array do - def get(arr, idx) do - Array.get(arr, idx) - end - - def get_and_update(arr, idx, fun) do - {get, update} = fun.(Array.get(arr, idx)) - {get, Array.set(arr, idx, update)} - end -end - defimpl Enumerable, for: Array do def count(arr), do: {:ok, Array.size(arr)} diff --git a/test/array_test.exs b/test/array_test.exs index 5d8af8b..ce8f60e 100644 --- a/test/array_test.exs +++ b/test/array_test.exs @@ -27,9 +27,7 @@ defmodule ArrayTest do assert nil == Array.get(a, 9) - assert_raise ArgumentError, fn -> - Array.get(a, 10) - end + assert nil == Array.get(a, 10) a = Array.set(a, 0, 1) assert 1 == Array.get(a, 0) @@ -87,7 +85,7 @@ defmodule ArrayTest do test "fix" do a = Array.new() a = Array.set(a, 100, 0) - + a = Array.fix(a) assert_raise ArgumentError, fn -> Array.set(a, 101, 0) @@ -293,22 +291,28 @@ defmodule ArrayTest do end end - test "get/set" do + test "get/fetch/set" do a = Array.new() a = Array.set(a, 5, 10) assert nil == Array.get(a, 4) + assert {:ok, nil} == Array.fetch(a, 4) assert 10 == Array.get(a, 5) + assert {:ok, 10} == Array.fetch(a, 5) assert nil == Array.get(a, 6) + assert :error == Array.fetch(a, 6) a = Array.set(a, 0, 100) assert 100 == Array.get(a, 0) assert_raise ArgumentError, fn -> Array.set(a, -1, 1000) end - assert_raise ArgumentError, fn -> - Array.get(a, -1) - end + assert nil == Array.get(a, -1) + assert :error == Array.fetch(a, -1) + + a = Array.from_list([]) + assert :error == Array.fetch(a, 0) + assert 0 == Array.get(a, 0, 0) end test "size" do @@ -405,7 +409,7 @@ defmodule ArrayTest do test "to_erlang_array" do a = Array.from_list([1,2,3]) ea = Array.to_erlang_array(a) - + assert :array.is_array(ea) assert 3 == :array.size(ea) assert 1 == :array.get(0, ea) @@ -423,7 +427,7 @@ defmodule ArrayTest do assert [{0, 1}, {1, 2}, {2, 3}] == Array.to_orddict(a) end - test "Access.get" do + test "Access.fetch" do a = Array.from_list([1,2,3]) assert 1 == a[0] assert 2 == a[1]