11defmodule Array do
2+ @ moduledoc """
3+ A wrapper module for Erlang's array.
4+ """
25 defstruct content: nil
36
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
419 def new ( ) do
520 % Array { content: :array . new ( { :default , nil } ) }
621 end
722
8- def new ( size ) do
9- % Array { content: :array . new ( size , { :default , nil } ) }
10- end
11-
12- def new ( size , options ) do
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
1342 if is_list ( options ) do
14- if List . keymember? ( options , :default , 0 ) do
15- % Array { content: :array . new ( size , options ) }
16- else
17- % Array { content: :array . new ( size , [ { :default , nil } | options ] ) }
18- end
43+ % Array { content: :array . new ( [ { :default , nil } | options ] ) }
1944 else
20- case options do
21- { :default , _ } -> % Array { content: :array . new ( size , options ) }
22- _ -> % Array { content: :array . new ( size , [ { :default , nil } , options ] ) }
23- end
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 )
2469 end
2570 end
2671
72+ @ doc """
73+ Gets the value used for uninitialized entries.
74+ """
75+ @ spec default ( t ) :: any
2776 def default ( % Array { content: c } ) ,
2877 do: :array . default ( c )
2978
79+ @ doc """
80+ Fixes the size of the array. This prevents it from growing automatically upon insertion.
81+ """
82+ @ spec fix ( t ) :: t
3083 def fix ( % Array { content: c } = arr ) ,
3184 do: % Array { arr | content: :array . fix ( c ) }
3285
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
3393 def foldl ( % Array { content: c } , acc , fun ) ,
3494 do: :array . foldl ( fun , acc , c )
3595
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
36103 def foldr ( % Array { content: c } , acc , fun ) ,
37104 do: :array . foldr ( fun , acc , c )
38105
106+ @ doc """
107+ Equivalent to `from_list(list, nil)`.
108+ """
109+ @ spec from_list ( list ) :: t
39110 def from_list ( list ) ,
40111 do: % Array { content: :array . from_list ( list , nil ) }
41112
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
42120 def from_list ( list , default ) ,
43121 do: % Array { content: :array . from_list ( list , default ) }
44122
123+ @ doc """
124+ Equivalent to `from_orddict(orddict, nil)`.
125+ """
126+ @ spec from_orddict ( orddict ) :: t
45127 def from_orddict ( orddict ) ,
46128 do: % Array { content: :array . from_orddict ( orddict , nil ) }
47129
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
48138 def from_orddict ( orddict , default ) ,
49139 do: % Array { content: :array . from_orddict ( orddict , default ) }
50140
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
51170 def is_array ( arr ) do
52171 case arr do
53172 % Array { content: c } -> :array . is_array ( c )
54173 _ -> false
55174 end
56175 end
57176
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
58181 def is_fix ( % Array { content: c } ) ,
59182 do: :array . is_fix ( c )
60183
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
61191 def map ( % Array { content: c } = arr , fun ) ,
62192 do: % Array { arr | content: :array . map ( fun , c ) }
63193
194+ @ doc """
195+ Makes the array resizable.
196+ """
197+ @ spec relax ( t ) :: t
64198 def relax ( % Array { content: c } = arr ) ,
65199 do: % Array { arr | content: :array . relax ( c ) }
66200
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
67210 def reset ( % Array { content: c } = arr , idx ) ,
68211 do: % Array { arr | content: :array . reset ( idx , c ) }
69212
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
70218 def resize ( % Array { content: c } = arr ) ,
71219 do: % Array { arr | content: :array . resize ( c ) }
72220
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
73227 def resize ( % Array { content: c } = arr , size ) ,
74228 do: % Array { arr | content: :array . resize ( size , c ) }
75229
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
76236 def set ( % Array { content: c } = arr , idx , val ) ,
77237 do: % Array { arr | content: :array . set ( idx , val , c ) }
78238
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
79245 def size ( % Array { content: c } ) ,
80246 do: :array . size ( c )
81247
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
82256 def sparse_foldl ( % Array { content: c } , acc , fun ) ,
83257 do: :array . sparse_foldl ( fun , acc , c )
84258
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
85267 def sparse_foldr ( % Array { content: c } , acc , fun ) ,
86268 do: :array . sparse_foldr ( fun , acc , c )
87269
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
88277 def sparse_map ( % Array { content: c } = arr , fun ) ,
89278 do: % Array { arr | content: :array . sparse_map ( fun , c ) }
90279
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
91286 def sparse_size ( % Array { content: c } ) ,
92287 do: :array . sparse_size ( c )
93288
289+ @ doc """
290+ Converts the array to a list, skipping default-valued entries.
291+ """
292+ @ spec sparse_to_list ( t ) :: list
94293 def sparse_to_list ( % Array { content: c } ) ,
95294 do: :array . sparse_to_list ( c )
96295
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 } ]
97300 def sparse_to_orddict ( % Array { content: c } ) ,
98301 do: :array . sparse_to_orddict ( c )
99302
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
100314 def to_list ( % Array { content: c } ) ,
101315 do: :array . to_list ( c )
102316
317+ @ doc """
318+ Converts the array to an ordered list of pairs `{index, value}`.
319+ """
320+ @ spec to_orddict ( t ) :: [ { index , element } ]
103321 def to_orddict ( % Array { content: c } ) ,
104322 do: :array . to_orddict ( c )
105-
106- def get ( % Array { content: c } , idx ) ,
107- do: :array . get ( idx , c )
108-
109- def get_and_update ( % Array { content: c } = arr , idx , fun ) do
110- { get , update } = fun . ( :array . get ( idx , c ) )
111- { get , % Array { arr | content: :array . set ( idx , update , c ) } }
112- end
113323end
114324
115325defimpl Access , for: Array do
@@ -118,12 +328,13 @@ defimpl Access, for: Array do
118328 end
119329
120330 def get_and_update ( arr , idx , fun ) do
121- Array . get_and_update ( arr , idx , fun )
331+ { get , update } = fun . ( Array . get ( arr , idx ) )
332+ { get , Array . set ( arr , idx , update ) }
122333 end
123334end
124335
125336defimpl Enumerable , for: Array do
126- def count ( arr ) , do: Array . size ( arr )
337+ def count ( arr ) , do: { :ok , Array . size ( arr ) }
127338
128339 def member? ( _arr , _value ) , do: { :error , __MODULE__ }
129340
0 commit comments