diff --git a/lib/array.ex b/lib/array.ex index 4fc9639..d07a71b 100644 --- a/lib/array.ex +++ b/lib/array.ex @@ -1,4 +1,5 @@ defmodule Array do + @behaviour Access @moduledoc """ A wrapper module for Erlang's array. """ @@ -15,7 +16,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 +86,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 +97,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 @@ -184,7 +185,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 +261,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 +271,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 @@ -320,16 +321,45 @@ defmodule Array do @spec to_orddict(t) :: [{index, element}] def to_orddict(%Array{content: c}), do: :array.to_orddict(c) -end -defimpl Access, for: Array do - def get(arr, idx) do - Array.get(arr, idx) + @doc """ + Access behavior `fetch/2` callback. + """ + def fetch(arr, idx) do + {:ok, get(arr, idx)} + end + + @doc """ + Access behavior `get/3` callback + """ + def get(arr, idx, value) do + if size(arr) < idx do + get(arr, idx) + else + value + end end + @doc """ + Access behavior `get_and_update/3` callback + """ def get_and_update(arr, idx, fun) do - {get, update} = fun.(Array.get(arr, idx)) - {get, Array.set(arr, idx, update)} + value = get(arr, idx) + case fun.(value) do + {get, update} -> {get, set(arr, idx, update)} + :pop -> {value, set(arr, idx, nil)} + end + end + + @doc """ + Access behavior `pop/2` callback + """ + def pop(arr, idx) do + if size(arr) < idx do + {get(arr, idx), set(arr, idx, nil)} + else + {nil, arr} + end end end @@ -341,6 +371,15 @@ defimpl Enumerable, for: Array do def reduce(%Array{content: c}, acc, fun) do Enumerable.reduce(:array.to_list(c), acc, fun) end + + def slice(arr) do + slice_fun = fn (start, length) -> + for i <- start..(start + length - 1) do + Array.get(arr, i) + end + end + {:ok, Array.size(arr), slice_fun} + end end defimpl Collectable, for: Array do diff --git a/mix.exs b/mix.exs index 0c3e169..1671cea 100644 --- a/mix.exs +++ b/mix.exs @@ -2,30 +2,18 @@ defmodule Array.Mixfile do use Mix.Project def project do - [app: :array, - version: "1.0.1", - elixir: ">= 1.0.0", + [app: :elixir_array, + version: "2.1.0", + elixir: ">= 1.11.1", description: "An elixir wrapper library for Erlang's array.", - package: package, - deps: deps] + package: package(), + deps: deps()] end - # Configuration for the OTP application - # - # Type `mix help compile.app` for more information def application do [applications: [:logger]] end - # Dependencies can be Hex packages: - # - # {:mydep, "~> 0.3.0"} - # - # Or git/path repositories: - # - # {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"} - # - # Type `mix help deps` for more examples and options defp deps do [{:earmark, ">= 0.0.0", only: :dev}, {:ex_doc, "~> 0.6", only: :dev}] diff --git a/test/array_test.exs b/test/array_test.exs index 5d8af8b..08322c1 100644 --- a/test/array_test.exs +++ b/test/array_test.exs @@ -87,7 +87,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) @@ -405,7 +405,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) @@ -454,6 +454,14 @@ defmodule ArrayTest do assert 6 == sum end + test "Enumerable.slice" do + slice = Enum.slice(Array.from_list([1,2,3,4,5]), 1, 2) + assert [2,3] == slice + + slice = Enum.slice(Array.from_list([1,2,3,4,5]), 3, 1) + assert [4] == slice + end + test "Collectable.into" do a = Enum.into([1,2,3], Array.new()) assert Array.is_array(a)