├── .gitignore ├── README.md ├── config └── config.exs ├── lib └── array.ex ├── mix.exs └── test ├── array_test.exs └── test_helper.exs /.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /docs 3 | /deps 4 | erl_crash.dump 5 | mix.lock 6 | *.ez 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Array 2 | 3 | An Elixir wrapper library for Erlang's array. 4 | 5 | Supports Access, Enumerable and Collectable protocols. 6 | 7 | ## Using Array with Mix 8 | To use array in your projects, add array as a dependency: 9 | 10 | ``` 11 | def deps do 12 | [{:array, "~> 1.0.1"}] 13 | end 14 | ``` 15 | 16 | Then run `mix deps.get` to install it. 17 | 18 | ## Documentation 19 | http://code.void.in/docs/elixir-array/ 20 | 21 | ## Example 22 | 23 | ``` 24 | # Create 25 | arr = Array.new() 26 | 27 | # Update 28 | arr = Array.set(arr, 0, 100) 29 | 30 | # Access by indices 31 | arr[0] # -> 0 32 | arr[1000] # -> nil 33 | 34 | # Convert from/to list 35 | Array.from_list([1,2,3,4,5]) 36 | Array.to_list(arr) 37 | 38 | # Transform using the Enum module 39 | Array.from_list([1,2,3,4,5]) |> Enum.map(fn x -> 2*x end) 40 | Enum.into(0..100, Array.new()) 41 | 42 | # Comprehension 43 | for v <- Array.from_list([1,2,3,4,5]), into: Array.new(), do: v*2 44 | ``` 45 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | use Mix.Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for third- 9 | # party users, it should be done in your mix.exs file. 10 | 11 | # Sample configuration: 12 | # 13 | # config :logger, 14 | # level: :info, 15 | # format: "$time $metadata[$level] $levelpad$message\n" 16 | 17 | # It is also possible to import configuration files, relative to this 18 | # directory. For example, you can emulate configuration per environment 19 | # by uncommenting the line below and defining dev.exs, test.exs and such. 20 | # Configuration from the imported file will override the ones defined 21 | # here (which is why it is important to import them last). 22 | # 23 | # import_config "#{Mix.env}.exs" 24 | -------------------------------------------------------------------------------- /lib/array.ex: -------------------------------------------------------------------------------- 1 | defmodule Array do 2 | @moduledoc """ 3 | A wrapper module for Erlang's array. 4 | """ 5 | defstruct content: nil 6 | 7 | @type t :: %__MODULE__{content: :array.array()} 8 | @type index :: non_neg_integer 9 | @type element :: any 10 | @type opts :: opt | [opt] 11 | @type opt :: {:fixed, boolean} | :fixed | {:default, any} | {:size, non_neg_integer} | non_neg_integer 12 | @type orddict :: [{index, element}] 13 | 14 | @doc """ 15 | Creates a new, extendible array with initial size zero. 16 | The default value is the atom nil, not undefined. 17 | """ 18 | @spec new() :: t 19 | def new() do 20 | %Array{content: :array.new({:default, nil})} 21 | end 22 | 23 | @doc """ 24 | Creates a new fixed array according to the given options. 25 | By default, the array is extendible and has initial size zero. 26 | The default value is the atom nil, if not specified. 27 | 28 | `options` is a single term or a list of terms, selected from the following: 29 | 30 | * `n : non_neg_integer` or `{:size, n : non_neg_integer}` 31 | * Specifies the initial size of the array; this also implies `{:fixed, true}`. 32 | If `n` is not a nonnegative integer, the call raises `ArgumentError`. 33 | * `:fixed` or `{:fixed, true}` 34 | * Creates a fixed-size array. 35 | * `{:fixed, false}` 36 | * Creates an extendible (non fixed-size) array. 37 | * `{:default, value}` 38 | * Sets the default value for the array to `value`. 39 | """ 40 | @spec new(opts) :: t 41 | def new(options) do 42 | if is_list(options) do 43 | %Array{content: :array.new([{:default, nil} | options])} 44 | else 45 | %Array{content: :array.new([{:default, nil}, options])} 46 | end 47 | end 48 | 49 | @doc """ 50 | Check if two arrays are equal using ===. 51 | """ 52 | @spec equal?(t, t) :: boolean 53 | def equal?(%Array{content: c1}, %Array{content: c2}) do 54 | s1 = :array.size(c1) 55 | s2 = :array.size(c2) 56 | cond do 57 | s1 != s2 -> false 58 | 59 | s1 <= 0 -> true 60 | 61 | true -> 62 | Enumerable.reduce(Range.new(0, s1-1), {:cont, true}, fn(idx, _acc) -> 63 | if :array.get(idx, c1) === :array.get(idx, c2) do 64 | {:cont, true} 65 | else 66 | {:halt, false} 67 | end 68 | end) |> elem(1) 69 | end 70 | end 71 | 72 | @doc """ 73 | Gets the value used for uninitialized entries. 74 | """ 75 | @spec default(t) :: any 76 | def default(%Array{content: c}), 77 | do: :array.default(c) 78 | 79 | @doc """ 80 | Fixes the size of the array. This prevents it from growing automatically upon insertion. 81 | """ 82 | @spec fix(t) :: t 83 | def fix(%Array{content: c} = arr), 84 | do: %Array{arr | content: :array.fix(c)} 85 | 86 | @doc """ 87 | Folds the elements of the array using the given function and initial accumulator value. 88 | The elements are visited in order from the lowest index to the highest. 89 | 90 | If `fun` is not a function, the call raises `ArgumentError`. 91 | """ 92 | @spec foldl(t, acc, (index, element, acc -> acc)) :: acc when acc: var 93 | def foldl(%Array{content: c}, acc, fun), 94 | do: :array.foldl(fun, acc, c) 95 | 96 | @doc """ 97 | Folds the elements of the array right-to-left using the given function and initial accumulator value. 98 | The elements are visited in order from the highest index to the lowest. 99 | 100 | If `fun` is not a function, the call raises `ArgumentError`. 101 | """ 102 | @spec foldr(t, acc, (index, element, acc -> acc)) :: acc when acc: var 103 | def foldr(%Array{content: c}, acc, fun), 104 | do: :array.foldr(fun, acc, c) 105 | 106 | @doc """ 107 | Equivalent to `from_list(list, nil)`. 108 | """ 109 | @spec from_list(list) :: t 110 | def from_list(list), 111 | do: %Array{content: :array.from_list(list, nil)} 112 | 113 | @doc """ 114 | Converts a list to an extendible array. 115 | `default` is used as the value for uninitialized entries of the array. 116 | 117 | If `list` is not a proper list, the call raises `ArgumentError`. 118 | """ 119 | @spec from_list(list, any) :: t 120 | def from_list(list, default), 121 | do: %Array{content: :array.from_list(list, default)} 122 | 123 | @doc """ 124 | Equivalent to `from_orddict(orddict, nil)`. 125 | """ 126 | @spec from_orddict(orddict) :: t 127 | def from_orddict(orddict), 128 | do: %Array{content: :array.from_orddict(orddict, nil)} 129 | 130 | @doc """ 131 | Converts an ordered list of pairs `{index, value}` to a corresponding extendible array. 132 | `default` is used as the value for uninitialized entries of the array. 133 | 134 | If `orddict` is not a proper, ordered list of pairs whose first elements are nonnegative integers, 135 | the call raises `ArgumentError`. 136 | """ 137 | @spec from_orddict(orddict, any) :: t 138 | def from_orddict(orddict, default), 139 | do: %Array{content: :array.from_orddict(orddict, default)} 140 | 141 | @doc """ 142 | Converts an Erlang's array to an array. 143 | All properties (size, elements, default value, fixedness) of the original array are preserved. 144 | 145 | If `erl_arr` is not an Erlang's array, the call raises `ArgumentError`. 146 | """ 147 | @spec from_erlang_array(:array.array()) :: t 148 | def from_erlang_array(erl_arr) do 149 | if :array.is_array(erl_arr) do 150 | %Array{content: erl_arr} 151 | else 152 | raise ArgumentError 153 | end 154 | end 155 | 156 | @doc """ 157 | Gets the value of entry `idx`. If `idx` is not a nonnegative integer, or if the array has 158 | fixed size and `idx` is larger than the maximum index, the call raises `ArgumentError`. 159 | """ 160 | @spec get(t, index) :: element 161 | def get(%Array{content: c}, idx), 162 | do: :array.get(idx, c) 163 | 164 | @doc """ 165 | Returns `true` if `arr` appears to be an array, otherwise `false`. 166 | Note that the check is only shallow; there is no guarantee that `arr` is a well-formed array 167 | representation even if this function returns `true`. 168 | """ 169 | @spec is_array(t) :: boolean 170 | def is_array(arr) do 171 | case arr do 172 | %Array{content: c} -> :array.is_array(c) 173 | _ -> false 174 | end 175 | end 176 | 177 | @doc """ 178 | Checks if the array has fixed size. Returns `true` if the array is fixed, otherwise `false`. 179 | """ 180 | @spec is_fix(t) :: boolean 181 | def is_fix(%Array{content: c}), 182 | do: :array.is_fix(c) 183 | 184 | @doc """ 185 | Maps the given function onto each element of the array. 186 | The elements are visited in order from the lowest index to the highest. 187 | 188 | If `fun` is not a function, the call raises `ArgumentError`. 189 | """ 190 | @spec map(t, (index, element -> any)) :: t 191 | def map(%Array{content: c} = arr, fun), 192 | do: %Array{arr | content: :array.map(fun, c)} 193 | 194 | @doc """ 195 | Makes the array resizable. 196 | """ 197 | @spec relax(t) :: t 198 | def relax(%Array{content: c} = arr), 199 | do: %Array{arr | content: :array.relax(c)} 200 | 201 | @doc """ 202 | Resets entry `idx` to the default value for the array. 203 | If the value of entry `idx` is the default value the array will be returned unchanged. 204 | Reset will never change size of the array. Shrinking can be done explicitly by calling `resize/2`. 205 | 206 | If `idx` is not a nonnegative integer, or if the array has fixed size and `idx` is 207 | larger than the maximum index, the call raises `ArgumentError`. 208 | """ 209 | @spec reset(t, index) :: t 210 | def reset(%Array{content: c} = arr, idx), 211 | do: %Array{arr | content: :array.reset(idx, c)} 212 | 213 | @doc """ 214 | Changes the size of the array to that reported by `sparse_size/1`. 215 | If the given array has fixed size, the resulting array will also have fixed size. 216 | """ 217 | @spec resize(t) :: t 218 | def resize(%Array{content: c} = arr), 219 | do: %Array{arr | content: :array.resize(c)} 220 | 221 | @doc """ 222 | Changes the size of the array. 223 | If `size` is not a nonnegative integer, the call raises `ArgumentError`. 224 | If the given array has fixed size, the resulting array will also have fixed size. 225 | """ 226 | @spec resize(t, non_neg_integer) :: t 227 | def resize(%Array{content: c} = arr, size), 228 | do: %Array{arr | content: :array.resize(size, c)} 229 | 230 | @doc """ 231 | Sets entry `idx` of the array to `val`. 232 | If `idx` is not a nonnegative integer, or if the array has fixed size and `idx` is 233 | larger than the maximum index, the call raises `ArgumentError`. 234 | """ 235 | @spec set(t, index, element) :: t 236 | def set(%Array{content: c} = arr, idx, val), 237 | do: %Array{arr | content: :array.set(idx, val, c)} 238 | 239 | @doc """ 240 | Gets the number of entries in the array. 241 | Entries are numbered from 0 to `size(array)-1`; hence, this is also the index of 242 | the first entry that is guaranteed to not have been previously set. 243 | """ 244 | @spec size(t) :: non_neg_integer 245 | def size(%Array{content: c}), 246 | do: :array.size(c) 247 | 248 | @doc """ 249 | Folds the elements of the array using the given function and initial accumulator value, 250 | skipping default-valued entries. 251 | The elements are visited in order from the lowest index to the highest. 252 | 253 | If `fun` is not a function, the call raises `ArgumentError`. 254 | """ 255 | @spec sparse_foldl(t, acc, (index, element, acc -> acc)) :: acc when acc: var 256 | def sparse_foldl(%Array{content: c}, acc, fun), 257 | do: :array.sparse_foldl(fun, acc, c) 258 | 259 | @doc """ 260 | Folds the elements of the array right-to-left using the given function and initial accumulator value, 261 | skipping default-valued entries. 262 | The elements are visited in order from the highest index to the lowest. 263 | 264 | If `fun` is not a function, the call raises `ArgumentError`. 265 | """ 266 | @spec sparse_foldr(t, acc, (index, element, acc -> acc)) :: acc when acc: var 267 | def sparse_foldr(%Array{content: c}, acc, fun), 268 | do: :array.sparse_foldr(fun, acc, c) 269 | 270 | @doc """ 271 | Maps the given function onto each element of the array, skipping default-valued entries. 272 | The elements are visited in order from the lowest index to the highest. 273 | 274 | If `fun` is not a function, the call raises `ArgumentError`. 275 | """ 276 | @spec sparse_map(t, (element -> any)) :: t 277 | def sparse_map(%Array{content: c} = arr, fun), 278 | do: %Array{arr | content: :array.sparse_map(fun, c)} 279 | 280 | @doc """ 281 | Gets the number of entries in the array up until the last non-default valued entry. 282 | In other words, returns `idx+1` if `idx` is the last non-default valued entry in the array, 283 | or zero if no such entry exists. 284 | """ 285 | @spec sparse_size(t) :: non_neg_integer 286 | def sparse_size(%Array{content: c}), 287 | do: :array.sparse_size(c) 288 | 289 | @doc """ 290 | Converts the array to a list, skipping default-valued entries. 291 | """ 292 | @spec sparse_to_list(t) :: list 293 | def sparse_to_list(%Array{content: c}), 294 | do: :array.sparse_to_list(c) 295 | 296 | @doc """ 297 | Converts the array to an ordered list of pairs `{index, value}`, skipping default-valued entries. 298 | """ 299 | @spec sparse_to_orddict(t) :: [{index, element}] 300 | def sparse_to_orddict(%Array{content: c}), 301 | do: :array.sparse_to_orddict(c) 302 | 303 | @doc """ 304 | Converts the array to its underlying Erlang's array. 305 | """ 306 | @spec to_erlang_array(t) :: :array.array() 307 | def to_erlang_array(%Array{content: c}), 308 | do: c 309 | 310 | @doc """ 311 | Converts the array to a list. 312 | """ 313 | @spec to_list(t) :: list 314 | def to_list(%Array{content: c}), 315 | do: :array.to_list(c) 316 | 317 | @doc """ 318 | Converts the array to an ordered list of pairs `{index, value}`. 319 | """ 320 | @spec to_orddict(t) :: [{index, element}] 321 | def to_orddict(%Array{content: c}), 322 | do: :array.to_orddict(c) 323 | end 324 | 325 | defimpl Access, for: Array do 326 | def get(arr, idx) do 327 | Array.get(arr, idx) 328 | end 329 | 330 | def get_and_update(arr, idx, fun) do 331 | {get, update} = fun.(Array.get(arr, idx)) 332 | {get, Array.set(arr, idx, update)} 333 | end 334 | end 335 | 336 | defimpl Enumerable, for: Array do 337 | def count(arr), do: {:ok, Array.size(arr)} 338 | 339 | def member?(_arr, _value), do: {:error, __MODULE__} 340 | 341 | def reduce(%Array{content: c}, acc, fun) do 342 | Enumerable.reduce(:array.to_list(c), acc, fun) 343 | end 344 | end 345 | 346 | defimpl Collectable, for: Array do 347 | def empty(_arr) do 348 | Array.new() 349 | end 350 | 351 | def into(original) do 352 | {[], fn 353 | list, {:cont, x} -> [x | list] 354 | list, :done -> Array.from_list(Array.to_list(original) ++ :lists.reverse(list)) 355 | _, :halt -> :ok 356 | end} 357 | end 358 | end 359 | 360 | defimpl Inspect, for: Array do 361 | import Inspect.Algebra 362 | 363 | def inspect(arr, opts) do 364 | concat(["#Array<", to_doc(Array.to_list(arr), opts), 365 | ", fixed=", Atom.to_string(Array.is_fix(arr)), 366 | ", default=", inspect(Array.default(arr)), 367 | ">"]) 368 | end 369 | end 370 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Array.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [app: :array, 6 | version: "1.0.1", 7 | elixir: ">= 1.0.0", 8 | description: "An elixir wrapper library for Erlang's array.", 9 | package: package, 10 | deps: deps] 11 | end 12 | 13 | # Configuration for the OTP application 14 | # 15 | # Type `mix help compile.app` for more information 16 | def application do 17 | [applications: [:logger]] 18 | end 19 | 20 | # Dependencies can be Hex packages: 21 | # 22 | # {:mydep, "~> 0.3.0"} 23 | # 24 | # Or git/path repositories: 25 | # 26 | # {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"} 27 | # 28 | # Type `mix help deps` for more examples and options 29 | defp deps do 30 | [{:earmark, ">= 0.0.0", only: :dev}, 31 | {:ex_doc, "~> 0.6", only: :dev}] 32 | end 33 | 34 | defp package do 35 | [files: ["lib", "mix.exs", "README*"], 36 | contributors: ["Kohei Takeda"], 37 | licenses: ["Apache 2.0"], 38 | links: %{"GitHub" => "https://github.com/takscape/elixir-array"}] 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /test/array_test.exs: -------------------------------------------------------------------------------- 1 | defmodule ArrayTest do 2 | use ExUnit.Case 3 | 4 | test "new/0" do 5 | a = Array.new() 6 | 7 | assert true == Array.is_array(a) 8 | assert false == Array.is_fix(a) 9 | assert nil == Array.default(a) 10 | 11 | assert nil == Array.get(a, 0) 12 | assert 0 == Array.size(a) 13 | a = Array.set(a, 0, 1) 14 | assert 1 == Array.get(a, 0) 15 | assert 1 == Array.size(a) 16 | end 17 | 18 | test "new/1, size specified" do 19 | a = Array.new(10) 20 | 21 | assert true == Array.is_array(a) 22 | assert true == Array.is_fix(a) 23 | assert nil == Array.default(a) 24 | assert 10 == Array.size(a) 25 | 26 | assert nil == Array.get(a, 0) 27 | 28 | assert nil == Array.get(a, 9) 29 | 30 | assert_raise ArgumentError, fn -> 31 | Array.get(a, 10) 32 | end 33 | 34 | a = Array.set(a, 0, 1) 35 | assert 1 == Array.get(a, 0) 36 | 37 | a = Array.set(a, 9, 9) 38 | assert 9 == Array.get(a, 9) 39 | 40 | assert_raise ArgumentError, fn -> 41 | Array.set(a, 10, 10) 42 | end 43 | end 44 | 45 | test "new/1, negative size" do 46 | Array.new(0) # This is not an error 47 | assert_raise ArgumentError, fn -> 48 | Array.new(-1) 49 | end 50 | end 51 | 52 | test "new/1, fixed" do 53 | a = Array.new(:fixed) 54 | 55 | assert true == Array.is_array(a) 56 | assert true == Array.is_fix(a) 57 | assert nil == Array.default(a) 58 | end 59 | 60 | test "new/1, default value specified" do 61 | a = Array.new([default: -1]) 62 | 63 | assert true == Array.is_array(a) 64 | assert false == Array.is_fix(a) 65 | assert -1 == Array.default(a) 66 | assert -1 == Array.get(a, 0) 67 | end 68 | 69 | test "default" do 70 | a = Array.new([default: "foo"]) 71 | assert "foo" == Array.default(a) 72 | 73 | a2 = Array.new() 74 | assert nil == Array.default(a2) 75 | end 76 | 77 | test "equal?" do 78 | assert Array.equal?(Array.from_list([1,2,3]), Array.from_list([1,2,3])) 79 | assert Array.equal?(Array.from_list([1, :foo, "bar"]), Array.from_list([1, :foo, "bar"])) 80 | assert Array.equal?(Array.new(), Array.new()) 81 | assert Array.equal?(Array.new(10), Array.new() |> Array.set(9, nil)) 82 | assert false == Array.equal?(Array.from_list([1,2,3]), Array.from_list([1,2,3,4])) 83 | assert false == Array.equal?(Array.from_list([2,2,3]), Array.from_list([1,2,3])) 84 | assert false == Array.equal?(Array.new(), Array.from_list(["a","b","c"])) 85 | end 86 | 87 | test "fix" do 88 | a = Array.new() 89 | a = Array.set(a, 100, 0) 90 | 91 | a = Array.fix(a) 92 | assert_raise ArgumentError, fn -> 93 | Array.set(a, 101, 0) 94 | end 95 | end 96 | 97 | test "foldl" do 98 | a = Array.from_list(["a", "b", "c"]) 99 | res = Array.foldl(a, "foo", fn idx, elm, acc -> 100 | case idx do 101 | 0 -> assert "a" == elm 102 | 1 -> assert "b" == elm 103 | 2 -> assert "c" == elm 104 | _ -> assert false 105 | end 106 | acc <> elm 107 | end) 108 | assert "fooabc" == res 109 | 110 | assert_raise ArgumentError, fn -> 111 | Array.foldl(a, "foo", "bar") 112 | end 113 | end 114 | 115 | test "foldr" do 116 | a = Array.from_list(["a", "b", "c"]) 117 | res = Array.foldr(a, "foo", fn idx, elm, acc -> 118 | case idx do 119 | 0 -> assert "a" == elm 120 | 1 -> assert "b" == elm 121 | 2 -> assert "c" == elm 122 | _ -> assert false 123 | end 124 | acc <> elm 125 | end) 126 | assert "foocba" == res 127 | 128 | assert_raise ArgumentError, fn -> 129 | Array.foldr(a, "foo", "bar") 130 | end 131 | end 132 | 133 | test "from_list/1" do 134 | a = Array.from_list([1,2,3]) 135 | 136 | assert true == Array.is_array(a) 137 | assert false == Array.is_fix(a) 138 | assert nil == Array.default(a) 139 | assert 3 == Array.size(a) 140 | 141 | assert 1 == Array.get(a, 0) 142 | assert 2 == Array.get(a, 1) 143 | assert 3 == Array.get(a, 2) 144 | end 145 | 146 | test "from_list/2" do 147 | a = Array.from_list([3,2,1], :foo) 148 | 149 | assert true == Array.is_array(a) 150 | assert false == Array.is_fix(a) 151 | assert :foo == Array.default(a) 152 | assert 3 == Array.size(a) 153 | 154 | assert 3 == Array.get(a, 0) 155 | assert 2 == Array.get(a, 1) 156 | assert 1 == Array.get(a, 2) 157 | end 158 | 159 | test "from_orddict/1" do 160 | a = Array.from_orddict([{1, "a"}, {3, "c"}, {4, "b"}]) 161 | 162 | assert true == Array.is_array(a) 163 | assert false == Array.is_fix(a) 164 | assert nil == Array.default(a) 165 | assert 5 == Array.size(a) 166 | 167 | assert nil == Array.get(a, 0) 168 | assert "a" == Array.get(a, 1) 169 | assert nil == Array.get(a, 2) 170 | assert "c" == Array.get(a, 3) 171 | assert "b" == Array.get(a, 4) 172 | assert nil == Array.get(a, 5) 173 | 174 | assert_raise ArgumentError, fn -> 175 | # unordered 176 | Array.from_orddict([{1, "a"}, {4, "b"}, {3, "c"}]) 177 | end 178 | end 179 | 180 | test "from_orddict/2" do 181 | a = Array.from_orddict([{1, "a"}, {3, "c"}, {4, "b"}], :foo) 182 | 183 | assert true == Array.is_array(a) 184 | assert false == Array.is_fix(a) 185 | assert :foo == Array.default(a) 186 | assert 5 == Array.size(a) 187 | 188 | assert :foo == Array.get(a, 0) 189 | assert "a" == Array.get(a, 1) 190 | assert :foo == Array.get(a, 2) 191 | assert "c" == Array.get(a, 3) 192 | assert "b" == Array.get(a, 4) 193 | assert :foo == Array.get(a, 5) 194 | 195 | assert_raise ArgumentError, fn -> 196 | # unordered 197 | Array.from_orddict([{1, "a"}, {4, "b"}, {3, "c"}], :foo) 198 | end 199 | end 200 | 201 | test "from_erlang_array" do 202 | erl_arr = :array.new() 203 | erl_arr = :array.set(0, 1, erl_arr) 204 | erl_arr = :array.set(1, 2, erl_arr) 205 | erl_arr = :array.set(2, 3, erl_arr) 206 | 207 | a = Array.from_erlang_array(erl_arr) 208 | assert true == Array.is_array(a) 209 | assert :array.size(erl_arr) == Array.size(a) 210 | assert :array.is_fix(erl_arr) == Array.is_fix(a) 211 | assert :array.default(erl_arr) == Array.default(a) 212 | assert 1 == Array.get(a, 0) 213 | assert 2 == Array.get(a, 1) 214 | assert 3 == Array.get(a, 2) 215 | 216 | assert_raise ArgumentError, fn -> 217 | Array.from_erlang_array([1,2,3]) 218 | end 219 | end 220 | 221 | test "is_array" do 222 | assert true == Array.is_array(Array.new()) 223 | assert false == Array.is_array(0) 224 | assert false == Array.is_array(nil) 225 | assert false == Array.is_array("foo") 226 | assert false == Array.is_array(:array.new()) 227 | end 228 | 229 | test "is_fix" do 230 | assert true == Array.is_fix(Array.new(:fixed)) 231 | assert false == Array.is_fix(Array.new([fixed: false])) 232 | end 233 | 234 | test "map" do 235 | a = Array.from_list([1,2,3]) 236 | a2 = Array.map(a, fn(idx, elm) -> 237 | case idx do 238 | 0 -> assert 1 == elm 239 | 1 -> assert 2 == elm 240 | 2 -> assert 3 == elm 241 | _ -> assert false 242 | end 243 | 2*elm 244 | end) 245 | assert [2,4,6] == Array.to_list(a2) 246 | 247 | assert_raise ArgumentError, fn -> 248 | Array.map(a, "foo") 249 | end 250 | end 251 | 252 | test "relax" do 253 | a = Array.new(:fixed) 254 | assert true == Array.is_fix(a) 255 | 256 | a = Array.relax(a) 257 | assert false == Array.is_fix(a) 258 | end 259 | 260 | test "reset" do 261 | a = Array.from_list([1,2,3]) 262 | assert 2 == Array.get(a, 1) 263 | 264 | a = Array.reset(a, 1) 265 | assert nil == Array.get(a, 1) 266 | end 267 | 268 | test "resize/1" do 269 | a = Array.new(10) 270 | assert 10 == Array.size(a) 271 | assert 0 == Array.sparse_size(a) 272 | 273 | a = Array.set(a, 8, 1) 274 | assert 10 == Array.size(a) 275 | assert 9 == Array.sparse_size(a) 276 | 277 | a = Array.resize(a) 278 | assert 9 == Array.size(a) 279 | assert 9 == Array.sparse_size(a) 280 | end 281 | 282 | test "resize/2" do 283 | a = Array.new([size: 10, fixed: true]) 284 | assert 10 == Array.size(a) 285 | 286 | a = Array.resize(a, 5) 287 | assert 5 == Array.size(a) 288 | assert true == Array.is_fix(a) 289 | assert false == (Array.new([size: 10, fixed: false]) |> Array.resize(5) |> Array.is_fix) 290 | 291 | assert_raise ArgumentError, fn -> 292 | Array.resize(Array.new(), -1) 293 | end 294 | end 295 | 296 | test "get/set" do 297 | a = Array.new() 298 | 299 | a = Array.set(a, 5, 10) 300 | assert nil == Array.get(a, 4) 301 | assert 10 == Array.get(a, 5) 302 | assert nil == Array.get(a, 6) 303 | 304 | a = Array.set(a, 0, 100) 305 | assert 100 == Array.get(a, 0) 306 | assert_raise ArgumentError, fn -> 307 | Array.set(a, -1, 1000) 308 | end 309 | assert_raise ArgumentError, fn -> 310 | Array.get(a, -1) 311 | end 312 | end 313 | 314 | test "size" do 315 | assert 10 == Array.new([size: 10]) |> Array.size 316 | assert 5 == Array.new([size: 5]) |> Array.size 317 | assert 6 == Array.new() |> Array.set(5,0) |> Array.size 318 | end 319 | 320 | test "sparse_foldl" do 321 | a = Array.new([size: 10, default: "x"]) 322 | a = a |> Array.set(2, "a") |> Array.set(4, "b") |> Array.set(6, "c") 323 | res = Array.sparse_foldl(a, "foo", fn idx, elm, acc -> 324 | case idx do 325 | 2 -> assert "a" == elm 326 | 4 -> assert "b" == elm 327 | 6 -> assert "c" == elm 328 | _ -> assert false 329 | end 330 | acc <> elm 331 | end) 332 | assert "fooabc" == res 333 | 334 | assert_raise ArgumentError, fn -> 335 | Array.sparse_foldl(a, "foo", "bar") 336 | end 337 | end 338 | 339 | test "sparse_foldr" do 340 | a = Array.new([size: 10, default: "x"]) 341 | a = a |> Array.set(1, "a") |> Array.set(3, "b") |> Array.set(5, "c") 342 | res = Array.sparse_foldr(a, "foo", fn idx, elm, acc -> 343 | case idx do 344 | 1 -> assert "a" == elm 345 | 3 -> assert "b" == elm 346 | 5 -> assert "c" == elm 347 | _ -> assert false 348 | end 349 | acc <> elm 350 | end) 351 | assert "foocba" == res 352 | 353 | assert_raise ArgumentError, fn -> 354 | Array.sparse_foldr(a, "foo", "bar") 355 | end 356 | end 357 | 358 | test "sparse_map" do 359 | a = Array.new([size: 10]) 360 | a = a |> Array.set(1, 2) |> Array.set(3, 4) |> Array.set(5, 6) 361 | res = Array.sparse_map(a, fn idx, elm -> 362 | case idx do 363 | 1 -> assert 2 == elm 364 | 3 -> assert 4 == elm 365 | 5 -> assert 6 == elm 366 | _ -> assert false 367 | end 368 | elm / 2 369 | end) 370 | Enum.each(0..9, fn idx -> 371 | case idx do 372 | 1 -> assert 1 == Array.get(res, idx) 373 | 3 -> assert 2 == Array.get(res, idx) 374 | 5 -> assert 3 == Array.get(res, idx) 375 | _ -> assert nil == Array.get(res, idx) 376 | end 377 | end) 378 | 379 | assert_raise ArgumentError, fn -> 380 | Array.sparse_map(a, "foo") 381 | end 382 | end 383 | 384 | test "sparse_size" do 385 | a = Array.from_list([1,2,3,4,5]) 386 | assert 5 == Array.sparse_size(a) 387 | a = Array.reset(a, 4) 388 | assert 4 == Array.sparse_size(a) 389 | end 390 | 391 | test "sparse_to_list" do 392 | a = Array.new([size: 10]) 393 | a = a |> Array.set(1, 1) |> Array.set(3, 2) |> Array.set(5, 3) 394 | 395 | assert [1,2,3] == Array.sparse_to_list(a) 396 | end 397 | 398 | test "sparse_to_orddict" do 399 | a = Array.new([size: 10]) 400 | a = a |> Array.set(2, 1) |> Array.set(4, 2) |> Array.set(6, 3) 401 | 402 | assert [{2,1}, {4,2}, {6,3}] == Array.sparse_to_orddict(a) 403 | end 404 | 405 | test "to_erlang_array" do 406 | a = Array.from_list([1,2,3]) 407 | ea = Array.to_erlang_array(a) 408 | 409 | assert :array.is_array(ea) 410 | assert 3 == :array.size(ea) 411 | assert 1 == :array.get(0, ea) 412 | assert 2 == :array.get(1, ea) 413 | assert 3 == :array.get(2, ea) 414 | end 415 | 416 | test "to_list" do 417 | a = Array.from_list([1,2,3]) 418 | assert [1,2,3] == Array.to_list(a) 419 | end 420 | 421 | test "to_orddict" do 422 | a = Array.from_list([1,2,3]) 423 | assert [{0, 1}, {1, 2}, {2, 3}] == Array.to_orddict(a) 424 | end 425 | 426 | test "Access.get" do 427 | a = Array.from_list([1,2,3]) 428 | assert 1 == a[0] 429 | assert 2 == a[1] 430 | assert 3 == a[2] 431 | assert nil == a[3] 432 | end 433 | 434 | test "Access.get_and_update" do 435 | a = Array.from_list([1,2,3]) 436 | {get, update} = Access.get_and_update(a, 1, fn v -> {2*v, 100} end) 437 | assert 4 == get 438 | assert [1,100,3] == Array.to_list(update) 439 | end 440 | 441 | test "Enumerable.count" do 442 | a = Array.from_list([1,2,3]) 443 | assert 3 == Enum.count(a) 444 | end 445 | 446 | test "Enumerable.member" do 447 | a = Array.from_list([1,2,3]) 448 | assert Enum.member?(a, 3) 449 | assert false == Enum.member?(a, 4) 450 | end 451 | 452 | test "Enumerable.reduce" do 453 | sum = Enum.reduce(Array.from_list([1,2,3]), 0, fn(x, acc) -> x + acc end) 454 | assert 6 == sum 455 | end 456 | 457 | test "Collectable.into" do 458 | a = Enum.into([1,2,3], Array.new()) 459 | assert Array.is_array(a) 460 | assert [1,2,3] == Array.to_list(a) 461 | end 462 | end 463 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | --------------------------------------------------------------------------------