1
1
defmodule Array do
2
+ @ moduledoc """
3
+ A wrapper module for Erlang's array.
4
+ """
2
5
defstruct content: nil
3
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
4
19
def new ( ) do
5
20
% Array { content: :array . new ( { :default , nil } ) }
6
21
end
7
22
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
13
42
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 ] ) }
19
44
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 )
24
69
end
25
70
end
26
71
72
+ @ doc """
73
+ Gets the value used for uninitialized entries.
74
+ """
75
+ @ spec default ( t ) :: any
27
76
def default ( % Array { content: c } ) ,
28
77
do: :array . default ( c )
29
78
79
+ @ doc """
80
+ Fixes the size of the array. This prevents it from growing automatically upon insertion.
81
+ """
82
+ @ spec fix ( t ) :: t
30
83
def fix ( % Array { content: c } = arr ) ,
31
84
do: % Array { arr | content: :array . fix ( c ) }
32
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
33
93
def foldl ( % Array { content: c } , acc , fun ) ,
34
94
do: :array . foldl ( fun , acc , c )
35
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
36
103
def foldr ( % Array { content: c } , acc , fun ) ,
37
104
do: :array . foldr ( fun , acc , c )
38
105
106
+ @ doc """
107
+ Equivalent to `from_list(list, nil)`.
108
+ """
109
+ @ spec from_list ( list ) :: t
39
110
def from_list ( list ) ,
40
111
do: % Array { content: :array . from_list ( list , nil ) }
41
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
42
120
def from_list ( list , default ) ,
43
121
do: % Array { content: :array . from_list ( list , default ) }
44
122
123
+ @ doc """
124
+ Equivalent to `from_orddict(orddict, nil)`.
125
+ """
126
+ @ spec from_orddict ( orddict ) :: t
45
127
def from_orddict ( orddict ) ,
46
128
do: % Array { content: :array . from_orddict ( orddict , nil ) }
47
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
48
138
def from_orddict ( orddict , default ) ,
49
139
do: % Array { content: :array . from_orddict ( orddict , default ) }
50
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
51
170
def is_array ( arr ) do
52
171
case arr do
53
172
% Array { content: c } -> :array . is_array ( c )
54
173
_ -> false
55
174
end
56
175
end
57
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
58
181
def is_fix ( % Array { content: c } ) ,
59
182
do: :array . is_fix ( c )
60
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
61
191
def map ( % Array { content: c } = arr , fun ) ,
62
192
do: % Array { arr | content: :array . map ( fun , c ) }
63
193
194
+ @ doc """
195
+ Makes the array resizable.
196
+ """
197
+ @ spec relax ( t ) :: t
64
198
def relax ( % Array { content: c } = arr ) ,
65
199
do: % Array { arr | content: :array . relax ( c ) }
66
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
67
210
def reset ( % Array { content: c } = arr , idx ) ,
68
211
do: % Array { arr | content: :array . reset ( idx , c ) }
69
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
70
218
def resize ( % Array { content: c } = arr ) ,
71
219
do: % Array { arr | content: :array . resize ( c ) }
72
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
73
227
def resize ( % Array { content: c } = arr , size ) ,
74
228
do: % Array { arr | content: :array . resize ( size , c ) }
75
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
76
236
def set ( % Array { content: c } = arr , idx , val ) ,
77
237
do: % Array { arr | content: :array . set ( idx , val , c ) }
78
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
79
245
def size ( % Array { content: c } ) ,
80
246
do: :array . size ( c )
81
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
82
256
def sparse_foldl ( % Array { content: c } , acc , fun ) ,
83
257
do: :array . sparse_foldl ( fun , acc , c )
84
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
85
267
def sparse_foldr ( % Array { content: c } , acc , fun ) ,
86
268
do: :array . sparse_foldr ( fun , acc , c )
87
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
88
277
def sparse_map ( % Array { content: c } = arr , fun ) ,
89
278
do: % Array { arr | content: :array . sparse_map ( fun , c ) }
90
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
91
286
def sparse_size ( % Array { content: c } ) ,
92
287
do: :array . sparse_size ( c )
93
288
289
+ @ doc """
290
+ Converts the array to a list, skipping default-valued entries.
291
+ """
292
+ @ spec sparse_to_list ( t ) :: list
94
293
def sparse_to_list ( % Array { content: c } ) ,
95
294
do: :array . sparse_to_list ( c )
96
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 } ]
97
300
def sparse_to_orddict ( % Array { content: c } ) ,
98
301
do: :array . sparse_to_orddict ( c )
99
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
100
314
def to_list ( % Array { content: c } ) ,
101
315
do: :array . to_list ( c )
102
316
317
+ @ doc """
318
+ Converts the array to an ordered list of pairs `{index, value}`.
319
+ """
320
+ @ spec to_orddict ( t ) :: [ { index , element } ]
103
321
def to_orddict ( % Array { content: c } ) ,
104
322
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
113
323
end
114
324
115
325
defimpl Access , for: Array do
@@ -118,12 +328,13 @@ defimpl Access, for: Array do
118
328
end
119
329
120
330
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 ) }
122
333
end
123
334
end
124
335
125
336
defimpl Enumerable , for: Array do
126
- def count ( arr ) , do: Array . size ( arr )
337
+ def count ( arr ) , do: { :ok , Array . size ( arr ) }
127
338
128
339
def member? ( _arr , _value ) , do: { :error , __MODULE__ }
129
340
0 commit comments