@grain/stdlib 0.5.13 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +193 -0
- package/LICENSE +1 -1
- package/README.md +25 -2
- package/array.gr +1512 -199
- package/array.md +2032 -94
- package/bigint.gr +239 -140
- package/bigint.md +450 -106
- package/buffer.gr +595 -102
- package/buffer.md +903 -145
- package/bytes.gr +401 -110
- package/bytes.md +551 -63
- package/char.gr +228 -49
- package/char.md +373 -7
- package/exception.gr +26 -12
- package/exception.md +29 -5
- package/float32.gr +130 -109
- package/float32.md +185 -57
- package/float64.gr +112 -99
- package/float64.md +185 -57
- package/hash.gr +47 -37
- package/hash.md +21 -3
- package/int16.gr +430 -0
- package/int16.md +618 -0
- package/int32.gr +200 -269
- package/int32.md +254 -289
- package/int64.gr +142 -225
- package/int64.md +254 -289
- package/int8.gr +511 -0
- package/int8.md +786 -0
- package/json.gr +2084 -0
- package/json.md +608 -0
- package/list.gr +120 -68
- package/list.md +125 -80
- package/map.gr +560 -57
- package/map.md +672 -56
- package/marshal.gr +239 -227
- package/marshal.md +36 -4
- package/number.gr +626 -676
- package/number.md +738 -153
- package/option.gr +33 -35
- package/option.md +58 -42
- package/package.json +2 -2
- package/path.gr +148 -187
- package/path.md +47 -96
- package/pervasives.gr +75 -416
- package/pervasives.md +85 -180
- package/priorityqueue.gr +433 -74
- package/priorityqueue.md +422 -54
- package/queue.gr +362 -80
- package/queue.md +433 -38
- package/random.gr +67 -75
- package/random.md +68 -40
- package/range.gr +135 -63
- package/range.md +198 -43
- package/rational.gr +284 -0
- package/rational.md +545 -0
- package/regex.gr +933 -1066
- package/regex.md +59 -60
- package/result.gr +23 -25
- package/result.md +54 -39
- package/runtime/atof/common.gr +78 -82
- package/runtime/atof/common.md +22 -10
- package/runtime/atof/decimal.gr +102 -127
- package/runtime/atof/decimal.md +28 -7
- package/runtime/atof/lemire.gr +56 -71
- package/runtime/atof/lemire.md +9 -1
- package/runtime/atof/parse.gr +83 -110
- package/runtime/atof/parse.md +12 -2
- package/runtime/atof/slow.gr +28 -35
- package/runtime/atof/slow.md +9 -1
- package/runtime/atof/table.gr +19 -18
- package/runtime/atof/table.md +10 -2
- package/runtime/atoi/parse.gr +153 -136
- package/runtime/atoi/parse.md +50 -1
- package/runtime/bigint.gr +410 -517
- package/runtime/bigint.md +71 -57
- package/runtime/compare.gr +176 -85
- package/runtime/compare.md +31 -1
- package/runtime/dataStructures.gr +144 -32
- package/runtime/dataStructures.md +267 -31
- package/runtime/debugPrint.gr +34 -15
- package/runtime/debugPrint.md +37 -5
- package/runtime/equal.gr +53 -52
- package/runtime/equal.md +30 -1
- package/runtime/exception.gr +38 -47
- package/runtime/exception.md +10 -8
- package/runtime/gc.gr +23 -152
- package/runtime/gc.md +13 -17
- package/runtime/malloc.gr +31 -31
- package/runtime/malloc.md +11 -3
- package/runtime/numberUtils.gr +191 -172
- package/runtime/numberUtils.md +17 -9
- package/runtime/numbers.gr +1695 -1021
- package/runtime/numbers.md +1098 -134
- package/runtime/string.gr +540 -242
- package/runtime/string.md +76 -6
- package/runtime/unsafe/constants.gr +30 -13
- package/runtime/unsafe/constants.md +80 -0
- package/runtime/unsafe/conv.gr +55 -28
- package/runtime/unsafe/conv.md +41 -9
- package/runtime/unsafe/memory.gr +10 -30
- package/runtime/unsafe/memory.md +15 -19
- package/runtime/unsafe/tags.gr +37 -21
- package/runtime/unsafe/tags.md +88 -8
- package/runtime/unsafe/wasmf32.gr +30 -36
- package/runtime/unsafe/wasmf32.md +64 -56
- package/runtime/unsafe/wasmf64.gr +30 -36
- package/runtime/unsafe/wasmf64.md +64 -56
- package/runtime/unsafe/wasmi32.gr +49 -66
- package/runtime/unsafe/wasmi32.md +102 -94
- package/runtime/unsafe/wasmi64.gr +52 -79
- package/runtime/unsafe/wasmi64.md +108 -100
- package/runtime/utils/printing.gr +13 -15
- package/runtime/utils/printing.md +11 -3
- package/runtime/wasi.gr +294 -295
- package/runtime/wasi.md +62 -42
- package/set.gr +574 -64
- package/set.md +634 -54
- package/stack.gr +181 -64
- package/stack.md +271 -42
- package/string.gr +453 -533
- package/string.md +241 -151
- package/uint16.gr +369 -0
- package/uint16.md +585 -0
- package/uint32.gr +470 -0
- package/uint32.md +737 -0
- package/uint64.gr +471 -0
- package/uint64.md +737 -0
- package/uint8.gr +369 -0
- package/uint8.md +585 -0
- package/uri.gr +1093 -0
- package/uri.md +477 -0
- package/{sys → wasi}/file.gr +914 -500
- package/{sys → wasi}/file.md +454 -50
- package/wasi/process.gr +292 -0
- package/{sys → wasi}/process.md +164 -6
- package/wasi/random.gr +77 -0
- package/wasi/random.md +80 -0
- package/{sys → wasi}/time.gr +15 -22
- package/{sys → wasi}/time.md +5 -5
- package/immutablearray.gr +0 -929
- package/immutablearray.md +0 -1038
- package/immutablemap.gr +0 -493
- package/immutablemap.md +0 -479
- package/immutablepriorityqueue.gr +0 -360
- package/immutablepriorityqueue.md +0 -291
- package/immutableset.gr +0 -498
- package/immutableset.md +0 -449
- package/runtime/debug.gr +0 -2
- package/runtime/debug.md +0 -6
- package/runtime/unsafe/errors.gr +0 -36
- package/runtime/unsafe/errors.md +0 -204
- package/sys/process.gr +0 -254
- package/sys/random.gr +0 -79
- package/sys/random.md +0 -66
package/array.gr
CHANGED
|
@@ -1,40 +1,55 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Utilities for working with arrays.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* An immutable array implementation is available in the `Immutable` submodule.
|
|
5
|
+
*
|
|
6
|
+
* @example from "array" include Array
|
|
7
|
+
* @example [> 1, 2, 3]
|
|
5
8
|
*
|
|
6
9
|
* @since v0.2.0
|
|
7
10
|
* @history v0.1.0: Originally named `arrays`
|
|
8
11
|
* @history v0.2.0: Renamed to `array`
|
|
9
12
|
*/
|
|
13
|
+
module Array
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
from "number" include Number
|
|
16
|
+
from "list" include List
|
|
17
|
+
from "runtime/unsafe/wasmi32" include WasmI32
|
|
18
|
+
from "runtime/unsafe/memory" include Memory
|
|
19
|
+
from "runtime/dataStructures" include DataStructures
|
|
20
|
+
use DataStructures.{ allocateArray, tagSimpleNumber }
|
|
21
|
+
from "runtime/exception" include Exception
|
|
22
|
+
from "runtime/numbers" include Numbers
|
|
23
|
+
use Numbers.{ coerceNumberToWasmI32 }
|
|
16
24
|
|
|
17
|
-
/**
|
|
18
|
-
* @section Values: Functions for working with the Array data type.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
@unsafe
|
|
22
|
-
let mut _ARRAY_LENGTH_OFFSET = 4n
|
|
23
25
|
@unsafe
|
|
24
|
-
let
|
|
26
|
+
let _ARRAY_START_OFFSET = 8n
|
|
25
27
|
|
|
26
28
|
@unsafe
|
|
27
29
|
let checkLength = length => {
|
|
30
|
+
use WasmI32.{ (&), (>>), (<) }
|
|
28
31
|
let ptr = WasmI32.fromGrain(length)
|
|
29
|
-
if (WasmI32.eqz(
|
|
32
|
+
if (WasmI32.eqz(ptr & 1n)) {
|
|
30
33
|
throw Exception.InvalidArgument("Length argument must be an integer")
|
|
31
34
|
}
|
|
32
|
-
let len =
|
|
33
|
-
if (
|
|
35
|
+
let len = ptr >> 1n
|
|
36
|
+
if (len < 0n) {
|
|
34
37
|
throw Exception.InvalidArgument("Length argument must be non-negative")
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
40
|
|
|
41
|
+
/**
|
|
42
|
+
* A simple helper function to convert a negative array index
|
|
43
|
+
* number to its positive positional equivalent.
|
|
44
|
+
*/
|
|
45
|
+
let wrapNegativeIndex = (arrLen, idx) => {
|
|
46
|
+
if (idx >= 0) {
|
|
47
|
+
idx
|
|
48
|
+
} else {
|
|
49
|
+
arrLen + idx
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
38
53
|
/**
|
|
39
54
|
* Provides the length of the input array.
|
|
40
55
|
*
|
|
@@ -45,10 +60,7 @@ let checkLength = length => {
|
|
|
45
60
|
* @since v0.1.0
|
|
46
61
|
*/
|
|
47
62
|
@unsafe
|
|
48
|
-
|
|
49
|
-
let ptr = WasmI32.fromGrain(array: Array<a>)
|
|
50
|
-
tagSimpleNumber(WasmI32.load(ptr, _ARRAY_LENGTH_OFFSET))
|
|
51
|
-
}
|
|
63
|
+
provide primitive length = "@array.length"
|
|
52
64
|
|
|
53
65
|
/**
|
|
54
66
|
* Creates a new array of the specified length with each element being
|
|
@@ -61,20 +73,20 @@ export let length = array => {
|
|
|
61
73
|
* @throws InvalidArgument(String): When `length` is not an integer
|
|
62
74
|
* @throws InvalidArgument(String): When `length` is negative
|
|
63
75
|
*
|
|
64
|
-
* @example Array.make(5, "foo")
|
|
76
|
+
* @example Array.make(5, "foo") == [> "foo", "foo", "foo", "foo", "foo"]
|
|
65
77
|
*
|
|
66
78
|
* @since v0.1.0
|
|
67
79
|
*/
|
|
68
80
|
@unsafe
|
|
69
|
-
|
|
70
|
-
|
|
81
|
+
provide let make = (length: Number, item: a) => {
|
|
82
|
+
use WasmI32.{ (+), (*), (<) }
|
|
71
83
|
checkLength(length)
|
|
72
84
|
let length = coerceNumberToWasmI32(length)
|
|
73
|
-
let byteLength =
|
|
85
|
+
let byteLength = length * 4n
|
|
74
86
|
let array = allocateArray(length)
|
|
75
|
-
for (let mut i = 0n;
|
|
87
|
+
for (let mut i = 0n; i < byteLength; i += 4n) {
|
|
76
88
|
WasmI32.store(
|
|
77
|
-
|
|
89
|
+
array + i,
|
|
78
90
|
Memory.incRef(WasmI32.fromGrain(item)),
|
|
79
91
|
_ARRAY_START_OFFSET
|
|
80
92
|
)
|
|
@@ -94,28 +106,25 @@ export let make: (Number, a) -> Array<a> = (length: Number, item: a) => {
|
|
|
94
106
|
* @throws InvalidArgument(String): When `length` is not an integer
|
|
95
107
|
* @throws InvalidArgument(String): When `length` is negative
|
|
96
108
|
*
|
|
97
|
-
* @example Array.init(5, n => n + 3)
|
|
109
|
+
* @example Array.init(5, n => n + 3) == [> 3, 4, 5, 6, 7]
|
|
98
110
|
*
|
|
99
111
|
* @since v0.1.0
|
|
100
112
|
*/
|
|
101
113
|
@unsafe
|
|
102
|
-
|
|
103
|
-
(
|
|
104
|
-
length: Number,
|
|
105
|
-
fn: Number -> a,
|
|
106
|
-
) => {
|
|
114
|
+
provide let init = (length: Number, fn: Number => a) => {
|
|
115
|
+
use WasmI32.{ (+), (*), (<) }
|
|
107
116
|
checkLength(length)
|
|
108
117
|
let length = coerceNumberToWasmI32(length)
|
|
109
|
-
let byteLength =
|
|
118
|
+
let byteLength = length * 4n
|
|
110
119
|
let array = allocateArray(length)
|
|
111
120
|
let mut index = 0n
|
|
112
|
-
for (let mut i = 0n;
|
|
121
|
+
for (let mut i = 0n; i < byteLength; i += 4n) {
|
|
113
122
|
WasmI32.store(
|
|
114
|
-
|
|
123
|
+
array + i,
|
|
115
124
|
Memory.incRef(WasmI32.fromGrain(fn(tagSimpleNumber(index)))),
|
|
116
125
|
_ARRAY_START_OFFSET
|
|
117
126
|
)
|
|
118
|
-
index
|
|
127
|
+
index += 1n
|
|
119
128
|
}
|
|
120
129
|
WasmI32.toGrain(array): Array<a>
|
|
121
130
|
}
|
|
@@ -129,12 +138,16 @@ export let init: (Number, Number -> a) -> Array<a> =
|
|
|
129
138
|
* @param index: The index to access
|
|
130
139
|
* @param array: The array to access
|
|
131
140
|
* @returns The element from the array
|
|
132
|
-
*
|
|
141
|
+
*
|
|
142
|
+
* @throws IndexOutOfBounds: When `index` is not an integer
|
|
143
|
+
* @throws IndexOutOfBounds: When `index` is out of bounds
|
|
144
|
+
*
|
|
145
|
+
* @example Array.get(1, [> 1, 2, 3, 4, 5]) == 2
|
|
133
146
|
*
|
|
134
147
|
* @since v0.1.0
|
|
135
148
|
* @history v0.2.0: Argument order changed to data-last
|
|
136
149
|
*/
|
|
137
|
-
|
|
150
|
+
provide let get = (index, array) => {
|
|
138
151
|
array[index]
|
|
139
152
|
}
|
|
140
153
|
|
|
@@ -147,12 +160,19 @@ export let get = (index, array) => {
|
|
|
147
160
|
* @param index: The index to update
|
|
148
161
|
* @param value: The value to store
|
|
149
162
|
* @param array: The array to update
|
|
150
|
-
*
|
|
163
|
+
*
|
|
164
|
+
* @throws IndexOutOfBounds: When `index` is not an integer
|
|
165
|
+
* @throws IndexOutOfBounds: When `index` is out of bounds
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* let array = [> 1, 2, 3, 4, 5]
|
|
169
|
+
* Array.set(1, 9, array)
|
|
170
|
+
* assert array == [> 1, 9, 3, 4, 5]
|
|
151
171
|
*
|
|
152
172
|
* @since v0.1.0
|
|
153
173
|
* @history v0.2.0: Argument order changed to data-last
|
|
154
174
|
*/
|
|
155
|
-
|
|
175
|
+
provide let set = (index, value, array) => {
|
|
156
176
|
array[index] = value
|
|
157
177
|
}
|
|
158
178
|
|
|
@@ -163,11 +183,14 @@ export let set = (index, value, array) => {
|
|
|
163
183
|
* @param array1: The array containing elements to appear first
|
|
164
184
|
* @param array2: The array containing elements to appear second
|
|
165
185
|
* @returns The new array containing elements from `array1` followed by elements from `array2`
|
|
186
|
+
*
|
|
187
|
+
* @throws InvalidArgument(String): When the combined length of the two arrays is not an integer
|
|
188
|
+
*
|
|
166
189
|
* @example Array.append([> 1, 2], [> 3, 4, 5]) == [> 1, 2, 3, 4, 5]
|
|
167
190
|
*
|
|
168
191
|
* @since v0.1.0
|
|
169
192
|
*/
|
|
170
|
-
|
|
193
|
+
provide let append = (array1, array2) => {
|
|
171
194
|
let len1 = length(array1)
|
|
172
195
|
let len2 = length(array2)
|
|
173
196
|
|
|
@@ -186,11 +209,14 @@ export let append = (array1, array2) => {
|
|
|
186
209
|
*
|
|
187
210
|
* @param arrays: A list containing all arrays to combine
|
|
188
211
|
* @returns The new array
|
|
212
|
+
*
|
|
213
|
+
* @throws InvalidArgument(String): When the combined length of all arrays is not an integer
|
|
214
|
+
*
|
|
189
215
|
* @example Array.concat([[> 1, 2], [> 3, 4], [> 5, 6]]) == [> 1, 2, 3, 4, 5, 6]
|
|
190
216
|
*
|
|
191
217
|
* @since v0.1.0
|
|
192
218
|
*/
|
|
193
|
-
|
|
219
|
+
provide let concat = arrays => {
|
|
194
220
|
// This function is slightly verbose to avoid depending on the List stdlib.
|
|
195
221
|
|
|
196
222
|
let rec findLength = (arrays, acc) => {
|
|
@@ -230,9 +256,11 @@ export let concat = arrays => {
|
|
|
230
256
|
* @param array: The array to copy
|
|
231
257
|
* @returns The new array containing the elements from the input
|
|
232
258
|
*
|
|
259
|
+
* @example Array.copy([> 1, 2, 3]) == [> 1, 2, 3]
|
|
260
|
+
*
|
|
233
261
|
* @since v0.1.0
|
|
234
262
|
*/
|
|
235
|
-
|
|
263
|
+
provide let copy = array => {
|
|
236
264
|
init(length(array), n => array[n])
|
|
237
265
|
}
|
|
238
266
|
|
|
@@ -243,9 +271,14 @@ export let copy = array => {
|
|
|
243
271
|
* @param n: The number of times to iterate the given array
|
|
244
272
|
* @param array: The array to iterate
|
|
245
273
|
*
|
|
274
|
+
* @example
|
|
275
|
+
* let mut str = ""
|
|
276
|
+
* Array.cycle(s => str = str ++ s, 2, [> "a", "b", "c"])
|
|
277
|
+
* assert str == "abcabc"
|
|
278
|
+
*
|
|
246
279
|
* @since v0.4.4
|
|
247
280
|
*/
|
|
248
|
-
|
|
281
|
+
provide let cycle = (fn, n, array) => {
|
|
249
282
|
let length = length(array)
|
|
250
283
|
for (let mut iteration = 0; iteration < n; iteration += 1) {
|
|
251
284
|
for (let mut count = 0; count < length; count += 1) {
|
|
@@ -260,10 +293,15 @@ export let cycle = (fn, n, array) => {
|
|
|
260
293
|
* @param fn: The iterator function to call with each element
|
|
261
294
|
* @param array: The array to iterate
|
|
262
295
|
*
|
|
296
|
+
* @example
|
|
297
|
+
* let mut str = ""
|
|
298
|
+
* Array.forEach(s => str = str ++ s, [> "a", "b", "c"])
|
|
299
|
+
* assert str == "abc"
|
|
300
|
+
*
|
|
263
301
|
* @since v0.1.0
|
|
264
302
|
* @history v0.2.0: Argument order changed to data-last
|
|
265
303
|
*/
|
|
266
|
-
|
|
304
|
+
provide let forEach = (fn, array) => {
|
|
267
305
|
let length = length(array)
|
|
268
306
|
for (let mut count = 0; count < length; count += 1) {
|
|
269
307
|
fn(array[count]): Void
|
|
@@ -277,10 +315,15 @@ export let forEach = (fn, array) => {
|
|
|
277
315
|
* @param fn: The iterator function to call with each element
|
|
278
316
|
* @param array: The array to iterate
|
|
279
317
|
*
|
|
318
|
+
* @example
|
|
319
|
+
* let mut str = ""
|
|
320
|
+
* Array.forEachi((s, i) => str = str ++ s ++ toString(i), [> "a", "b", "c"])
|
|
321
|
+
* assert str == "a0b1c2"
|
|
322
|
+
*
|
|
280
323
|
* @since v0.1.0
|
|
281
324
|
* @history v0.2.0: Argument order changed to data-last
|
|
282
325
|
*/
|
|
283
|
-
|
|
326
|
+
provide let forEachi = (fn, array) => {
|
|
284
327
|
let length = length(array)
|
|
285
328
|
for (let mut count = 0; count < length; count += 1) {
|
|
286
329
|
fn(array[count], count): Void
|
|
@@ -295,10 +338,12 @@ export let forEachi = (fn, array) => {
|
|
|
295
338
|
* @param array: The array to iterate
|
|
296
339
|
* @returns The new array with mapped values
|
|
297
340
|
*
|
|
341
|
+
* @example Array.map(x => x * 2, [> 1, 2, 3]) == [> 2, 4, 6]
|
|
342
|
+
*
|
|
298
343
|
* @since v0.1.0
|
|
299
344
|
* @history v0.2.0: Argument order changed to data-last
|
|
300
345
|
*/
|
|
301
|
-
|
|
346
|
+
provide let map = (fn, array) => {
|
|
302
347
|
let length = length(array)
|
|
303
348
|
init(length, i => {
|
|
304
349
|
fn(array[i])
|
|
@@ -313,9 +358,11 @@ export let map = (fn, array) => {
|
|
|
313
358
|
* @param array: The array to iterate
|
|
314
359
|
* @returns The new array with mapped values
|
|
315
360
|
*
|
|
361
|
+
* @example Array.mapi((x, i) => (x * 2, i), [> 1, 2, 3]) == [> (2, 0), (4, 1), (6, 2)]
|
|
362
|
+
*
|
|
316
363
|
* @since v0.1.0
|
|
317
364
|
*/
|
|
318
|
-
|
|
365
|
+
provide let mapi = (fn, array) => {
|
|
319
366
|
let length = length(array)
|
|
320
367
|
init(length, index => {
|
|
321
368
|
fn(array[index], index)
|
|
@@ -336,11 +383,12 @@ export let mapi = (fn, array) => {
|
|
|
336
383
|
* @param array: The array to iterate
|
|
337
384
|
* @returns The final accumulator returned from `fn`
|
|
338
385
|
*
|
|
339
|
-
* @example Array.reduce((
|
|
386
|
+
* @example Array.reduce((acc, el) => acc + el, 0, [> 1, 2, 3]) == 6
|
|
387
|
+
* @example Array.reduce((acc, el) => acc ++ el, "", [> "baz", "bar", "foo"]) == "bazbarfoo"
|
|
340
388
|
*
|
|
341
389
|
* @since v0.3.0
|
|
342
390
|
*/
|
|
343
|
-
|
|
391
|
+
provide let reduce = (fn, initial, array) => {
|
|
344
392
|
let mut acc = initial
|
|
345
393
|
forEach(el => acc = fn(acc, el), array)
|
|
346
394
|
acc
|
|
@@ -360,11 +408,11 @@ export let reduce = (fn, initial, array) => {
|
|
|
360
408
|
* @param array: The array to iterate
|
|
361
409
|
* @returns The final accumulator returned from `fn`
|
|
362
410
|
*
|
|
363
|
-
* @example Array.reduceRight((
|
|
411
|
+
* @example Array.reduceRight((el, acc) => acc ++ el, "", [> "baz", "bar", "foo"]) == "foobarbaz"
|
|
364
412
|
*
|
|
365
413
|
* @since v0.5.3
|
|
366
414
|
*/
|
|
367
|
-
|
|
415
|
+
provide let reduceRight = (fn, initial, array) => {
|
|
368
416
|
let mut acc = initial
|
|
369
417
|
for (let mut i = length(array) - 1; i >= 0; i -= 1) {
|
|
370
418
|
acc = fn(array[i], acc)
|
|
@@ -387,9 +435,16 @@ export let reduceRight = (fn, initial, array) => {
|
|
|
387
435
|
* @param array: The array to iterate
|
|
388
436
|
* @returns The final accumulator returned from `fn`
|
|
389
437
|
*
|
|
438
|
+
* @example Array.reducei((acc, el, index) => acc + el + index, 0, [> 1, 2, 3]) == 9
|
|
439
|
+
* @example
|
|
440
|
+
* let output = Array.reducei((acc, el, index) => {
|
|
441
|
+
* acc ++ el ++ toString(index)
|
|
442
|
+
* }, "", [> "baz", "bar", "foo"])
|
|
443
|
+
* assert output == "baz0bar1foo2"
|
|
444
|
+
*
|
|
390
445
|
* @since v0.3.0
|
|
391
446
|
*/
|
|
392
|
-
|
|
447
|
+
provide let reducei = (fn, initial, array) => {
|
|
393
448
|
let mut acc = initial
|
|
394
449
|
forEachi((el, index) => acc = fn(acc, el, index), array)
|
|
395
450
|
acc
|
|
@@ -405,9 +460,13 @@ export let reducei = (fn, initial, array) => {
|
|
|
405
460
|
* @param array: The array to iterate
|
|
406
461
|
* @returns The new array
|
|
407
462
|
*
|
|
463
|
+
* @throws InvalidArgument(String): When the combined length of all arrays is not an integer
|
|
464
|
+
*
|
|
465
|
+
* @example Array.flatMap(e => [> 1, e], [> 1, 2, 3]) == [> 1, 1, 1, 2, 1, 3]
|
|
466
|
+
*
|
|
408
467
|
* @since v0.3.0
|
|
409
468
|
*/
|
|
410
|
-
|
|
469
|
+
provide let flatMap = (fn, array) => {
|
|
411
470
|
let nested = map(fn, array)
|
|
412
471
|
let arrLen = reduce((acc, arr) => acc + length(arr), 0, nested)
|
|
413
472
|
let mut outerI = 0
|
|
@@ -429,11 +488,14 @@ export let flatMap = (fn, array) => {
|
|
|
429
488
|
*
|
|
430
489
|
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
431
490
|
* @param array: The array to check
|
|
432
|
-
* @returns `true` if all elements
|
|
491
|
+
* @returns `true` if all elements satisfy the condition or `false` otherwise
|
|
492
|
+
*
|
|
493
|
+
* @example Array.every(e => e % 2 == 0, [> 2, 4, 6]) == true
|
|
494
|
+
* @example Array.every(e => e % 2 == 0, [> 2, 4, 7]) == false
|
|
433
495
|
*
|
|
434
496
|
* @since v0.3.0
|
|
435
497
|
*/
|
|
436
|
-
|
|
498
|
+
provide let every = (fn, array) => {
|
|
437
499
|
let len = length(array)
|
|
438
500
|
let mut all = true
|
|
439
501
|
for (let mut index = 0; all && index < len; index += 1) {
|
|
@@ -448,11 +510,15 @@ export let every = (fn, array) => {
|
|
|
448
510
|
*
|
|
449
511
|
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
450
512
|
* @param array: The array to iterate
|
|
451
|
-
* @returns `true` if one or more elements
|
|
513
|
+
* @returns `true` if one or more elements satisfy the condition or `false` otherwise
|
|
514
|
+
*
|
|
515
|
+
* @example Array.some(e => e % 2 == 0, [> 2, 4, 6]) == true
|
|
516
|
+
* @example Array.some(e => e % 2 == 0, [> 2, 4, 7]) == true
|
|
517
|
+
* @example Array.some(e => e % 2 == 0, [> 3, 5, 7]) == false
|
|
452
518
|
*
|
|
453
519
|
* @since v0.3.0
|
|
454
520
|
*/
|
|
455
|
-
|
|
521
|
+
provide let some = (fn, array) => {
|
|
456
522
|
let len = length(array)
|
|
457
523
|
let mut found = false
|
|
458
524
|
for (let mut index = 0; !found && index < len; index += 1) {
|
|
@@ -467,9 +533,14 @@ export let some = (fn, array) => {
|
|
|
467
533
|
* @param value: The value replacing each element
|
|
468
534
|
* @param array: The array to update
|
|
469
535
|
*
|
|
536
|
+
* @example
|
|
537
|
+
* let arr = [> 2, 4, 6]
|
|
538
|
+
* Array.fill(0, arr)
|
|
539
|
+
* assert arr == [> 0, 0, 0]
|
|
540
|
+
*
|
|
470
541
|
* @since v0.2.0
|
|
471
542
|
*/
|
|
472
|
-
|
|
543
|
+
provide let fill = (value, array) => {
|
|
473
544
|
let length = length(array)
|
|
474
545
|
forEachi((_, index) => {
|
|
475
546
|
array[index] = value
|
|
@@ -486,26 +557,32 @@ export let fill = (value, array) => {
|
|
|
486
557
|
* @param stop: The (exclusive) index to end replacement
|
|
487
558
|
* @param array: The array to update
|
|
488
559
|
*
|
|
560
|
+
* @throws IndexOutOfBounds: When the start index is out of bounds
|
|
561
|
+
* @throws IndexOutOfBounds: When the start index is greater then the stop index
|
|
562
|
+
*
|
|
563
|
+
* @example
|
|
564
|
+
* let arr = [> 2, 4, 6, 8]
|
|
565
|
+
* Array.fillRange(0, 1, 3, arr)
|
|
566
|
+
* assert arr == [> 2, 0, 0, 8]
|
|
567
|
+
*
|
|
489
568
|
* @since v0.2.0
|
|
490
569
|
*/
|
|
491
|
-
|
|
570
|
+
provide let fillRange = (value, start, stop, array) => {
|
|
492
571
|
let length = length(array)
|
|
493
|
-
let startIndex =
|
|
494
|
-
let stopIndex =
|
|
495
|
-
|
|
496
|
-
if (
|
|
497
|
-
|
|
572
|
+
let startIndex = wrapNegativeIndex(length, start)
|
|
573
|
+
let stopIndex = wrapNegativeIndex(length, stop)
|
|
574
|
+
// Ensure we aren't working with a `stop` value that is too big
|
|
575
|
+
let stopIndex = if (stopIndex < 0 || stopIndex > length) {
|
|
576
|
+
length
|
|
577
|
+
} else {
|
|
578
|
+
stopIndex
|
|
498
579
|
}
|
|
499
|
-
|
|
500
|
-
|
|
580
|
+
|
|
581
|
+
if (startIndex > length || startIndex > stopIndex) {
|
|
582
|
+
throw IndexOutOfBounds
|
|
501
583
|
}
|
|
502
584
|
|
|
503
|
-
let mut index = startIndex
|
|
504
|
-
for (
|
|
505
|
-
let mut index = startIndex;
|
|
506
|
-
index < stopIndex && index < length;
|
|
507
|
-
index += 1
|
|
508
|
-
) {
|
|
585
|
+
for (let mut index = startIndex; index < stopIndex; index += 1) {
|
|
509
586
|
array[index] = value
|
|
510
587
|
}
|
|
511
588
|
void
|
|
@@ -517,9 +594,11 @@ export let fillRange = (value, start, stop, array) => {
|
|
|
517
594
|
* @param array: The array to reverse
|
|
518
595
|
* @returns The new array
|
|
519
596
|
*
|
|
597
|
+
* @example Array.reverse([> 1, 2, 3]) == [> 3, 2, 1]
|
|
598
|
+
*
|
|
520
599
|
* @since v0.4.0
|
|
521
600
|
*/
|
|
522
|
-
|
|
601
|
+
provide let reverse = array => {
|
|
523
602
|
let len = length(array)
|
|
524
603
|
init(len, index => {
|
|
525
604
|
let last = len - index - 1
|
|
@@ -533,9 +612,11 @@ export let reverse = array => {
|
|
|
533
612
|
* @param array: The array to convert
|
|
534
613
|
* @returns The list containing all elements from the array
|
|
535
614
|
*
|
|
615
|
+
* @example Array.toList([> 1, 2, 3]) == [1, 2, 3]
|
|
616
|
+
*
|
|
536
617
|
* @since v0.1.0
|
|
537
618
|
*/
|
|
538
|
-
|
|
619
|
+
provide let toList = array => {
|
|
539
620
|
let rec buildList = (acc, index) => {
|
|
540
621
|
let index = index - 1
|
|
541
622
|
if (index < 0) {
|
|
@@ -553,9 +634,11 @@ export let toList = array => {
|
|
|
553
634
|
* @param list: The list to convert
|
|
554
635
|
* @returns The array containing all elements from the list
|
|
555
636
|
*
|
|
637
|
+
* @example Array.fromList([1, 2, 3]) == [> 1, 2, 3]
|
|
638
|
+
*
|
|
556
639
|
* @since v0.1.0
|
|
557
640
|
*/
|
|
558
|
-
|
|
641
|
+
provide let fromList = list => {
|
|
559
642
|
let rec listLength = (list, acc) => {
|
|
560
643
|
match (list) {
|
|
561
644
|
[_, ...rest] => listLength(rest, acc + 1),
|
|
@@ -584,84 +667,62 @@ export let fromList = list => {
|
|
|
584
667
|
* @param array: The array to inspect
|
|
585
668
|
* @returns `true` if the value exists in the array or `false` otherwise
|
|
586
669
|
*
|
|
670
|
+
* @example Array.contains(1, [> 1, 2, 3]) == true
|
|
671
|
+
* @example Array.contains(0, [> 1, 2, 3]) == false
|
|
672
|
+
*
|
|
587
673
|
* @since v0.2.0
|
|
588
674
|
*/
|
|
589
|
-
|
|
675
|
+
provide let contains = (search, array) => {
|
|
590
676
|
// TODO(#189): This should be rewritten to use recursion and pattern matching
|
|
591
677
|
let len = length(array)
|
|
592
|
-
let mut
|
|
593
|
-
|
|
594
|
-
found = array[index] == search
|
|
678
|
+
for (let mut i = 0; i < len; i += 1) {
|
|
679
|
+
if (array[i] == search) return true
|
|
595
680
|
}
|
|
596
|
-
|
|
681
|
+
return false
|
|
597
682
|
}
|
|
598
683
|
|
|
599
684
|
/**
|
|
600
|
-
* Finds the first element in an array that
|
|
685
|
+
* Finds the first element in an array that satisfies the given condition.
|
|
601
686
|
*
|
|
602
687
|
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
603
688
|
* @param array: The array to search
|
|
604
689
|
* @returns `Some(element)` containing the first value found or `None` otherwise
|
|
605
690
|
*
|
|
691
|
+
* @example Array.find(e => e % 2 == 0, [> 1, 4, 3]) == Some(4)
|
|
692
|
+
* @example Array.find(e => e % 2 == 0, [> 1, 2, 3, 4]) == Some(2)
|
|
693
|
+
* @example Array.find(e => e % 2 == 0, [> 1, 3, 5]) == None
|
|
694
|
+
*
|
|
606
695
|
* @since v0.2.0
|
|
607
696
|
*/
|
|
608
|
-
|
|
609
|
-
let
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
let mut count = 0
|
|
614
|
-
let mut matching = false
|
|
615
|
-
let mut matchedItem = array[0]
|
|
616
|
-
while (count < length) {
|
|
617
|
-
if (fn(array[count])) {
|
|
618
|
-
matching = true
|
|
619
|
-
matchedItem = array[count]
|
|
620
|
-
count = length
|
|
621
|
-
} else {
|
|
622
|
-
count += 1
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
if (!matching) {
|
|
626
|
-
None
|
|
627
|
-
} else {
|
|
628
|
-
Some(matchedItem)
|
|
697
|
+
provide let find = (fn, array) => {
|
|
698
|
+
let len = length(array)
|
|
699
|
+
for (let mut i = 0; i < len; i += 1) {
|
|
700
|
+
if (fn(array[i])) {
|
|
701
|
+
return Some(array[i])
|
|
629
702
|
}
|
|
630
703
|
}
|
|
704
|
+
return None
|
|
631
705
|
}
|
|
632
706
|
|
|
633
707
|
/**
|
|
634
|
-
* Finds the first index in an array where the element
|
|
708
|
+
* Finds the first index in an array where the element satisfies the given condition.
|
|
635
709
|
*
|
|
636
710
|
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
637
711
|
* @param array: The array to search
|
|
638
712
|
* @returns `Some(index)` containing the index of the first element found or `None` otherwise
|
|
639
713
|
*
|
|
714
|
+
* @example Array.findIndex(e => e % 2 == 0, [> 1, 4, 3]) == Some(1)
|
|
715
|
+
* @example Array.findIndex(e => e % 2 == 0, [> 1, 2, 3, 4]) == Some(1)
|
|
716
|
+
* @example Array.findIndex(e => e % 2 == 0, [> 1, 3, 5]) == None
|
|
717
|
+
*
|
|
640
718
|
* @since v0.2.0
|
|
641
719
|
*/
|
|
642
|
-
|
|
643
|
-
let
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
} else {
|
|
647
|
-
let mut count = 0
|
|
648
|
-
let mut matching = false
|
|
649
|
-
let mut matchedIndex = 0
|
|
650
|
-
while (count < length) {
|
|
651
|
-
if (fn(array[count])) {
|
|
652
|
-
matching = true
|
|
653
|
-
matchedIndex = count
|
|
654
|
-
count = length
|
|
655
|
-
} else {
|
|
656
|
-
count += 1
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
if (!matching) {
|
|
660
|
-
None
|
|
661
|
-
} else {
|
|
662
|
-
Some(matchedIndex)
|
|
663
|
-
}
|
|
720
|
+
provide let findIndex = (fn, array) => {
|
|
721
|
+
let len = length(array)
|
|
722
|
+
for (let mut i = 0; i < len; i += 1) {
|
|
723
|
+
if (fn(array[i])) return Some(i)
|
|
664
724
|
}
|
|
725
|
+
return None
|
|
665
726
|
}
|
|
666
727
|
|
|
667
728
|
/**
|
|
@@ -672,9 +733,13 @@ export let findIndex = (fn, array) => {
|
|
|
672
733
|
* @param array2: The array to provide values for the second tuple element
|
|
673
734
|
* @returns The new array containing all pairs of `(a, b)`
|
|
674
735
|
*
|
|
736
|
+
* @throws InvalidArgument(String): When the multiplied array lengths are not an integer
|
|
737
|
+
*
|
|
738
|
+
* @example Array.product([> 1, 2], [> 3, 4]) == [> (1, 3), (1, 4), (2, 3), (2, 4)]
|
|
739
|
+
*
|
|
675
740
|
* @since v0.2.0
|
|
676
741
|
*/
|
|
677
|
-
|
|
742
|
+
provide let product = (array1: Array<a>, array2: Array<b>) => {
|
|
678
743
|
let lenA = length(array1)
|
|
679
744
|
let lenB = length(array2)
|
|
680
745
|
let mut indexA = -1
|
|
@@ -682,8 +747,6 @@ export let product = (array1: Array<a>, array2: Array<b>) => {
|
|
|
682
747
|
init(lenA * lenB, n => {
|
|
683
748
|
if (n % lenB == 0) {
|
|
684
749
|
indexA += 1
|
|
685
|
-
} else {
|
|
686
|
-
indexA = indexA
|
|
687
750
|
}
|
|
688
751
|
(array1[indexA], array2[n % lenB])
|
|
689
752
|
})
|
|
@@ -696,18 +759,19 @@ export let product = (array1: Array<a>, array2: Array<b>) => {
|
|
|
696
759
|
* @param array: The array to iterate
|
|
697
760
|
* @returns The total number of elements that satisfy the condition
|
|
698
761
|
*
|
|
762
|
+
* @example Array.count(e => e % 2 == 0, [> 1, 2, 3, 4]) == 2
|
|
763
|
+
*
|
|
699
764
|
* @since v0.2.0
|
|
700
765
|
*/
|
|
701
|
-
|
|
702
|
-
let
|
|
703
|
-
let mut
|
|
704
|
-
let mut
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
count += 1
|
|
766
|
+
provide let count = (fn, array) => {
|
|
767
|
+
let len = length(array)
|
|
768
|
+
let mut n = 0
|
|
769
|
+
for (let mut i = 0; i < len; i += 1) {
|
|
770
|
+
if (fn(array[i])) {
|
|
771
|
+
n += 1
|
|
708
772
|
}
|
|
709
773
|
}
|
|
710
|
-
|
|
774
|
+
n
|
|
711
775
|
}
|
|
712
776
|
|
|
713
777
|
/**
|
|
@@ -718,17 +782,20 @@ export let count = (fn, array) => {
|
|
|
718
782
|
* @param array: The array to iterate
|
|
719
783
|
* @returns The total number of elements that satisfy the condition
|
|
720
784
|
*
|
|
785
|
+
* @example Array.counti((e, i) => e % 2 == 0 && i % 2 == 0, [> 1, 2, 3, 4, 5]) == 0
|
|
786
|
+
* @example Array.counti((e, i) => e % 2 == 0 && i % 2 == 0, [> 0, 1, 2, 3, 5]) == 2
|
|
787
|
+
*
|
|
721
788
|
* @since v0.3.0
|
|
722
789
|
*/
|
|
723
|
-
|
|
724
|
-
let
|
|
725
|
-
let mut
|
|
726
|
-
for (let mut
|
|
727
|
-
if (fn(array[
|
|
728
|
-
|
|
790
|
+
provide let counti = (fn, array) => {
|
|
791
|
+
let len = length(array)
|
|
792
|
+
let mut n = 0
|
|
793
|
+
for (let mut i = 0; i < len; i += 1) {
|
|
794
|
+
if (fn(array[i], i)) {
|
|
795
|
+
n += 1
|
|
729
796
|
}
|
|
730
797
|
}
|
|
731
|
-
|
|
798
|
+
n
|
|
732
799
|
}
|
|
733
800
|
|
|
734
801
|
/**
|
|
@@ -740,9 +807,11 @@ export let counti = (fn, array) => {
|
|
|
740
807
|
* @param array: The array to iterate
|
|
741
808
|
* @returns The new array containing elements where `fn` returned `true`
|
|
742
809
|
*
|
|
810
|
+
* @example Array.filter(e => e % 2 == 0, [> 1, 2, 3, 4]) == [> 2, 4]
|
|
811
|
+
*
|
|
743
812
|
* @since v0.3.0
|
|
744
813
|
*/
|
|
745
|
-
|
|
814
|
+
provide let filter = (fn, array) => {
|
|
746
815
|
let filtered = copy(array)
|
|
747
816
|
let mut position = 0
|
|
748
817
|
forEach(el => {
|
|
@@ -765,9 +834,12 @@ export let filter = (fn, array) => {
|
|
|
765
834
|
* @param array: The array to iterate
|
|
766
835
|
* @returns The new array containing elements where `fn` returned `true`
|
|
767
836
|
*
|
|
837
|
+
* @example Array.filteri((e, i) => e % 2 == 0, [> 1, 2, 3, 4]) == [> 2, 4]
|
|
838
|
+
* @example Array.filteri((e, i) => e % 2 == 1 && i % 2 == 0, [> 1, 2, 3, 4, 5]) == [> 1, 3, 5]
|
|
839
|
+
*
|
|
768
840
|
* @since v0.3.0
|
|
769
841
|
*/
|
|
770
|
-
|
|
842
|
+
provide let filteri = (fn, array) => {
|
|
771
843
|
let filtered = copy(array)
|
|
772
844
|
let mut position = 0
|
|
773
845
|
forEachi((el, index) => {
|
|
@@ -788,11 +860,16 @@ export let filteri = (fn, array) => {
|
|
|
788
860
|
* @param array: The array to filter
|
|
789
861
|
* @returns The new array with only unique values
|
|
790
862
|
*
|
|
863
|
+
* @example Array.unique([> 1, 2, 1, 2, 3, 1]) == [> 1, 2, 3]
|
|
864
|
+
*
|
|
791
865
|
* @since v0.3.0
|
|
792
866
|
*/
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
867
|
+
provide let unique = array => {
|
|
868
|
+
// TODO(#1651): improve performance
|
|
869
|
+
filteri(
|
|
870
|
+
(el, index) => findIndex(value => value == el, array) == Some(index),
|
|
871
|
+
array
|
|
872
|
+
)
|
|
796
873
|
}
|
|
797
874
|
|
|
798
875
|
/**
|
|
@@ -804,19 +881,20 @@ export let unique = array => {
|
|
|
804
881
|
* @param array2: The array to provide values for the second tuple element
|
|
805
882
|
* @returns The new array containing indexed pairs of `(a, b)`
|
|
806
883
|
*
|
|
807
|
-
* @throws
|
|
884
|
+
* @throws IndexOutOfBounds: When the arrays have different sizes
|
|
885
|
+
*
|
|
886
|
+
* @example Array.zip([> 1, 2, 3], [> 4, 5, 6]) == [> (1, 4), (2, 5), (3, 6)]
|
|
808
887
|
*
|
|
809
888
|
* @since v0.4.0
|
|
889
|
+
* @history v0.6.0: Support zipping arrays of different sizes
|
|
810
890
|
*/
|
|
811
|
-
|
|
891
|
+
provide let zip = (array1: Array<a>, array2: Array<b>) => {
|
|
812
892
|
let len = length(array1)
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
})
|
|
819
|
-
}
|
|
893
|
+
let len2 = length(array2)
|
|
894
|
+
let len = if (len > len2) len2 else len
|
|
895
|
+
init(len, n => {
|
|
896
|
+
(array1[n], array2[n])
|
|
897
|
+
})
|
|
820
898
|
}
|
|
821
899
|
|
|
822
900
|
/**
|
|
@@ -834,12 +912,14 @@ export let zip = (array1: Array<a>, array2: Array<b>) => {
|
|
|
834
912
|
* @param array2: The array whose elements will each be passed to the function as the second argument
|
|
835
913
|
* @returns The new array containing elements derived from applying the function to pairs of input array elements
|
|
836
914
|
*
|
|
837
|
-
* @
|
|
838
|
-
*
|
|
915
|
+
* @throws IndexOutOfBounds: When the arrays have different sizes
|
|
916
|
+
*
|
|
917
|
+
* @example Array.zipWith((a, b) => a + b, [> 1, 2, 3], [> 4, 5, 6]) == [> 5, 7, 9]
|
|
918
|
+
* @example Array.zipWith((a, b) => a * b, [> 1, 2, 3], [> 4, 5]) == [> 4, 10]
|
|
839
919
|
*
|
|
840
920
|
* @since v0.5.3
|
|
841
921
|
*/
|
|
842
|
-
|
|
922
|
+
provide let zipWith = (fn, array1: Array<a>, array2: Array<b>) => {
|
|
843
923
|
let len1 = length(array1)
|
|
844
924
|
let len2 = length(array2)
|
|
845
925
|
let minLen = if (len1 > len2) len2 else len1
|
|
@@ -854,9 +934,11 @@ export let zipWith = (fn, array1: Array<a>, array2: Array<b>) => {
|
|
|
854
934
|
* @param array: The array of tuples to split
|
|
855
935
|
* @returns An array containing all elements from the first tuple element, and an array containing all elements from the second tuple element
|
|
856
936
|
*
|
|
937
|
+
* @example Array.unzip([> (1, 4), (2, 5), (3, 6)]) == ([> 1, 2, 3], [> 4, 5, 6])
|
|
938
|
+
*
|
|
857
939
|
* @since v0.4.0
|
|
858
940
|
*/
|
|
859
|
-
|
|
941
|
+
provide let unzip = array => {
|
|
860
942
|
let lenArr = length(array)
|
|
861
943
|
|
|
862
944
|
let a = init(lenArr, n => {
|
|
@@ -879,31 +961,34 @@ export let unzip = array => {
|
|
|
879
961
|
* @param items: The input strings
|
|
880
962
|
* @returns The concatenated string
|
|
881
963
|
*
|
|
964
|
+
* @example Array.join(", ", [> "a", "b", "c"]) == "a, b, c"
|
|
965
|
+
*
|
|
882
966
|
* @since v0.4.0
|
|
883
967
|
*/
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
968
|
+
@unsafe
|
|
969
|
+
provide let join = (separator: String, items: Array<String>) => {
|
|
970
|
+
use WasmI32.{ (+), (==) }
|
|
971
|
+
use DataStructures.{ allocateString, stringSize }
|
|
972
|
+
let arrLen = length(items)
|
|
973
|
+
let sepPtr = WasmI32.fromGrain(separator)
|
|
974
|
+
let sepSize = stringSize(sepPtr)
|
|
975
|
+
let sepPtr = sepPtr + 8n
|
|
976
|
+
let mut strSize = 0n
|
|
977
|
+
for (let mut i = 0; i < arrLen; i = incr(i)) {
|
|
978
|
+
strSize += stringSize(WasmI32.fromGrain(items[i]))
|
|
979
|
+
if (i != 0) strSize += sepSize
|
|
894
980
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
} else {
|
|
905
|
-
arrLen + idx
|
|
981
|
+
let newString = allocateString(strSize)
|
|
982
|
+
let mut offset = newString + 8n
|
|
983
|
+
for (let mut i = 0; i < arrLen; i = incr(i)) {
|
|
984
|
+
let ptr = WasmI32.fromGrain(items[i])
|
|
985
|
+
let size = stringSize(ptr)
|
|
986
|
+
Memory.copy(offset, ptr + 8n, size)
|
|
987
|
+
offset += size
|
|
988
|
+
if (i != arrLen) Memory.copy(offset, sepPtr, sepSize)
|
|
989
|
+
offset += sepSize
|
|
906
990
|
}
|
|
991
|
+
WasmI32.toGrain(newString): String
|
|
907
992
|
}
|
|
908
993
|
|
|
909
994
|
/**
|
|
@@ -913,17 +998,21 @@ let wrapNegativeIndex = (arrLen, idx) => {
|
|
|
913
998
|
* If either index is a negative number, it will be treated as a reverse index from
|
|
914
999
|
* the end of the array. e.g. `slice(1, -1, [> 'a', 'b', 'c']) == [> 'b']`.
|
|
915
1000
|
*
|
|
916
|
-
* @param
|
|
917
|
-
* @param
|
|
1001
|
+
* @param start: The index of the array where the slice will begin (inclusive)
|
|
1002
|
+
* @param end: The index of the array where the slice will end (exclusive)
|
|
918
1003
|
* @param array: The array to be sliced
|
|
919
1004
|
* @returns The subset of the array that was sliced
|
|
920
1005
|
*
|
|
1006
|
+
* @example Array.slice(1, end=3, [> 1, 2, 3, 4]) == [> 2, 3]
|
|
1007
|
+
* @example Array.slice(1, [> 1, 2, 3, 4]) == [> 2, 3, 4]
|
|
1008
|
+
*
|
|
921
1009
|
* @since v0.4.0
|
|
1010
|
+
* @history v0.6.0: Default `end` to the Array length
|
|
922
1011
|
*/
|
|
923
|
-
|
|
1012
|
+
provide let slice = (start, end=length(array), array) => {
|
|
924
1013
|
let arrayLength = length(array)
|
|
925
|
-
let startIndex = wrapNegativeIndex(arrayLength,
|
|
926
|
-
let endIndex = wrapNegativeIndex(arrayLength,
|
|
1014
|
+
let startIndex = wrapNegativeIndex(arrayLength, start)
|
|
1015
|
+
let endIndex = wrapNegativeIndex(arrayLength, end)
|
|
927
1016
|
// Ensure we aren't working with an `end` value that is too big
|
|
928
1017
|
let endIndex = if (endIndex > arrayLength) {
|
|
929
1018
|
arrayLength
|
|
@@ -945,16 +1034,23 @@ export let slice = (startIndex, endIndex, array) => {
|
|
|
945
1034
|
* Sorts an array in-place.
|
|
946
1035
|
*
|
|
947
1036
|
* Ordering is calculated using a comparator function which takes two array elements and must return 0 if both are equal, a positive number if the first is greater, and a negative number if the first is smaller.
|
|
948
|
-
* @param
|
|
1037
|
+
* @param compare: The comparator function used to indicate sort order
|
|
949
1038
|
* @param array: The array to be sorted
|
|
1039
|
+
*
|
|
1040
|
+
* @example
|
|
1041
|
+
* let arr = [> 3, 2, 4, 1]
|
|
1042
|
+
* Array.sort(compare=(a, b) => a - b, arr)
|
|
1043
|
+
* assert arr == [> 1, 2, 3, 4]
|
|
1044
|
+
*
|
|
950
1045
|
* @since v0.4.5
|
|
1046
|
+
* @history v0.6.0: Made `compare` a default argument
|
|
951
1047
|
*/
|
|
952
|
-
|
|
1048
|
+
provide let sort = (compare=compare, array) => {
|
|
953
1049
|
let partition = (low, high) => {
|
|
954
1050
|
let pivot = array[high]
|
|
955
1051
|
let mut i = low - 1
|
|
956
1052
|
for (let mut j = low; j < high; j += 1) {
|
|
957
|
-
if (
|
|
1053
|
+
if (compare(array[j], pivot) < 0) {
|
|
958
1054
|
i += 1
|
|
959
1055
|
let temp = array[i]
|
|
960
1056
|
array[i] = array[j]
|
|
@@ -978,19 +1074,28 @@ export let sort = (comp, array) => {
|
|
|
978
1074
|
}
|
|
979
1075
|
|
|
980
1076
|
/**
|
|
981
|
-
* Rotates array elements by the specified amount to the
|
|
1077
|
+
* Rotates array elements in place by the specified amount to the left, such
|
|
1078
|
+
* that the `n`th element becomes the first in the array.
|
|
982
1079
|
*
|
|
983
1080
|
* If value is negative, array elements will be rotated by the
|
|
984
|
-
* specified amount to the
|
|
1081
|
+
* specified amount to the right. See examples.
|
|
985
1082
|
*
|
|
986
1083
|
* @param n: The number of elements to rotate by
|
|
987
1084
|
* @param arr: The array to be rotated
|
|
988
1085
|
*
|
|
989
|
-
* @example
|
|
990
|
-
*
|
|
1086
|
+
* @example
|
|
1087
|
+
* let array = [> 1, 2, 3, 4, 5]
|
|
1088
|
+
* Array.rotate(2, array)
|
|
1089
|
+
* assert array == [> 3, 4, 5, 1, 2]
|
|
1090
|
+
* @example
|
|
1091
|
+
* let array = [> 1, 2, 3, 4, 5]
|
|
1092
|
+
* Array.rotate(-1, array)
|
|
1093
|
+
* assert array == [> 5, 1, 2, 3, 4]
|
|
1094
|
+
*
|
|
991
1095
|
* @since v0.4.5
|
|
1096
|
+
* @history v0.6.0: Behavior changed from right-rotation to left-rotation
|
|
992
1097
|
*/
|
|
993
|
-
|
|
1098
|
+
provide let rotate = (n, arr) => {
|
|
994
1099
|
let rec gcd = (a, b) => {
|
|
995
1100
|
if (b == 0) {
|
|
996
1101
|
a
|
|
@@ -1002,13 +1107,12 @@ export let rotate = (n, arr) => {
|
|
|
1002
1107
|
let arrLen = length(arr)
|
|
1003
1108
|
if (arrLen > 0) {
|
|
1004
1109
|
let k = n % arrLen
|
|
1005
|
-
let mut d = -1
|
|
1006
1110
|
let mut j = 0
|
|
1007
1111
|
for (let mut i = 0; i < gcd(arrLen, k); i += 1) {
|
|
1008
1112
|
j = i
|
|
1009
1113
|
let temp = arr[i]
|
|
1010
1114
|
while (true) {
|
|
1011
|
-
d = (j
|
|
1115
|
+
let d = (j + k) % arrLen
|
|
1012
1116
|
if (d == i) {
|
|
1013
1117
|
break
|
|
1014
1118
|
}
|
|
@@ -1020,3 +1124,1212 @@ export let rotate = (n, arr) => {
|
|
|
1020
1124
|
}
|
|
1021
1125
|
}
|
|
1022
1126
|
}
|
|
1127
|
+
|
|
1128
|
+
let rec chunkHelp = (chunkSize, arr, arrLen, chunkStartIndex) => {
|
|
1129
|
+
if (arrLen == 0) {
|
|
1130
|
+
[]
|
|
1131
|
+
} else if (arrLen < chunkSize) {
|
|
1132
|
+
[slice(chunkStartIndex, arr)]
|
|
1133
|
+
} else {
|
|
1134
|
+
// create the first chunk of the given array
|
|
1135
|
+
let firstChunk = slice(
|
|
1136
|
+
chunkStartIndex,
|
|
1137
|
+
end=chunkStartIndex + chunkSize,
|
|
1138
|
+
arr
|
|
1139
|
+
)
|
|
1140
|
+
let restChunks = chunkHelp(
|
|
1141
|
+
chunkSize,
|
|
1142
|
+
arr,
|
|
1143
|
+
arrLen - chunkSize,
|
|
1144
|
+
chunkStartIndex + chunkSize
|
|
1145
|
+
)
|
|
1146
|
+
[firstChunk, ...restChunks]
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* Splits the given array into chunks of the provided size.
|
|
1152
|
+
* If the array cannot be split evenly, the final chunk will contain the remaining elements.
|
|
1153
|
+
*
|
|
1154
|
+
* @param chunkSize: The maximum size of each chunk
|
|
1155
|
+
* @param arr: The array to chunk
|
|
1156
|
+
* @returns An array of chunks
|
|
1157
|
+
*
|
|
1158
|
+
* @throws InvalidArgument(String): When `chunkSize` is not an integer
|
|
1159
|
+
* @throws InvalidArgument(String): When `chunkSize` is less than one
|
|
1160
|
+
*
|
|
1161
|
+
* @example Array.chunk(2, [> 1, 2, 3, 4, 5]) == [> [> 1, 2], [> 3, 4], [> 5]]
|
|
1162
|
+
* @example Array.chunk(2, [> 1, 2, 3, 4]) == [> [> 1, 2], [> 3, 4]]
|
|
1163
|
+
*
|
|
1164
|
+
* @since v0.6.0
|
|
1165
|
+
*/
|
|
1166
|
+
provide let chunk = (chunkSize, arr) => {
|
|
1167
|
+
if (chunkSize <= 0) {
|
|
1168
|
+
throw Exception.InvalidArgument("chunkSize must be greater than 0")
|
|
1169
|
+
} else {
|
|
1170
|
+
checkLength(chunkSize)
|
|
1171
|
+
let arrLen = length(arr)
|
|
1172
|
+
let chunks = chunkHelp(chunkSize, arr, arrLen, 0)
|
|
1173
|
+
fromList(chunks)
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/**
|
|
1178
|
+
* An immutable array implementation.
|
|
1179
|
+
*
|
|
1180
|
+
* @example use Array.{ module Immutable }
|
|
1181
|
+
* @example Array.Immutable.empty
|
|
1182
|
+
* @example Immutable.fromList([1, 2, 3, 4, 5])
|
|
1183
|
+
*
|
|
1184
|
+
* @since v0.6.0
|
|
1185
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1186
|
+
*/
|
|
1187
|
+
provide module Immutable {
|
|
1188
|
+
// Immutable arrays implemented as relaxed radix balanced trees. This data
|
|
1189
|
+
// structure allows access and updating in O(log(n)) time, but since the tree
|
|
1190
|
+
// branching factor is chosen to be a large number (32), these operations run
|
|
1191
|
+
// in effectively constant time in most practical situations.
|
|
1192
|
+
|
|
1193
|
+
// This implementation was adapted from Elm's Array module
|
|
1194
|
+
// https://github.com/elm/core/blob/master/src/Array.elm
|
|
1195
|
+
// license of software used:
|
|
1196
|
+
|
|
1197
|
+
// Copyright 2014-present Evan Czaplicki
|
|
1198
|
+
//
|
|
1199
|
+
// Redistribution and use in source and binary forms, with or without
|
|
1200
|
+
// modification, are permitted provided that the following conditions are met:
|
|
1201
|
+
//
|
|
1202
|
+
// 1. Redistributions of source code must retain the above copyright notice,
|
|
1203
|
+
// this list of conditions and the following disclaimer.
|
|
1204
|
+
//
|
|
1205
|
+
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
1206
|
+
// this list of conditions and the following disclaimer in the documentation
|
|
1207
|
+
// and/or other materials provided with the distribution.
|
|
1208
|
+
//
|
|
1209
|
+
// 3. Neither the name of the copyright holder nor the names of its
|
|
1210
|
+
// contributors may be used to endorse or promote products derived from this
|
|
1211
|
+
// software without specific prior written permission.
|
|
1212
|
+
//
|
|
1213
|
+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
1214
|
+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
1215
|
+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
1216
|
+
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
1217
|
+
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
1218
|
+
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
1219
|
+
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
1220
|
+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
1221
|
+
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
1222
|
+
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
1223
|
+
// POSSIBILITY OF SUCH DAMAGE.
|
|
1224
|
+
|
|
1225
|
+
// Maximum number of children each tree node can have;
|
|
1226
|
+
// an arbitrary multiple of 2 that gives a good performance tradeoff
|
|
1227
|
+
let branchingFactor = 32
|
|
1228
|
+
// 32 = 2^5
|
|
1229
|
+
let branchingBits = 5
|
|
1230
|
+
// To be applied to numbers to bring them within the range [0, 32)
|
|
1231
|
+
let bitmask = branchingFactor - 1
|
|
1232
|
+
|
|
1233
|
+
type rec Tree<a> = Array<Node<a>>
|
|
1234
|
+
and enum Node<a> {
|
|
1235
|
+
Tree(Tree<a>),
|
|
1236
|
+
Leaf(Array<a>),
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
// a helper data structure used for building an array piece by piece
|
|
1240
|
+
record Builder<a> {
|
|
1241
|
+
btail: Array<a>,
|
|
1242
|
+
nodes: List<Node<a>>,
|
|
1243
|
+
numNodes: Number,
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
// A "tail" of < 32 values at the end of the array is kept as a performance
|
|
1247
|
+
// optimization
|
|
1248
|
+
abstract record ImmutableArray<a> {
|
|
1249
|
+
length: Number,
|
|
1250
|
+
shift: Number,
|
|
1251
|
+
root: Tree<a>,
|
|
1252
|
+
tail: Array<a>,
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
// Aliasing names to make references to mutable arrays within the Immutable
|
|
1256
|
+
// submodule clearer to distinguish
|
|
1257
|
+
let mutLength = length
|
|
1258
|
+
let mutInit = init
|
|
1259
|
+
let mutForEach = forEach
|
|
1260
|
+
let mutMap = map
|
|
1261
|
+
let mutReduce = reduce
|
|
1262
|
+
let mutReduceRight = reduceRight
|
|
1263
|
+
let mutFromList = fromList
|
|
1264
|
+
let mutCopy = copy
|
|
1265
|
+
let mutSlice = slice
|
|
1266
|
+
let mutAppend = append
|
|
1267
|
+
|
|
1268
|
+
/**
|
|
1269
|
+
* An empty array.
|
|
1270
|
+
*
|
|
1271
|
+
* @example Array.Immutable.empty == Array.Immutable.fromList([])
|
|
1272
|
+
*
|
|
1273
|
+
* @since v0.6.0
|
|
1274
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1275
|
+
*/
|
|
1276
|
+
provide let empty = {
|
|
1277
|
+
let empty = { length: 0, shift: branchingBits, root: [>], tail: [>] }
|
|
1278
|
+
empty
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
/**
|
|
1282
|
+
* Determines if the array contains no elements.
|
|
1283
|
+
*
|
|
1284
|
+
* @param array: The array to check
|
|
1285
|
+
* @returns `true` if the array is empty and `false` otherwise
|
|
1286
|
+
*
|
|
1287
|
+
* @example
|
|
1288
|
+
* use Array.{ module Immutable }
|
|
1289
|
+
* assert Immutable.isEmpty(Immutable.fromList([1, 2, 3])) == false
|
|
1290
|
+
* @example
|
|
1291
|
+
* use Array.{ module Immutable }
|
|
1292
|
+
* assert Immutable.isEmpty(Immutable.fromList([])) == true
|
|
1293
|
+
*
|
|
1294
|
+
* @since v0.6.0
|
|
1295
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1296
|
+
*/
|
|
1297
|
+
provide let isEmpty = array => array.length == 0
|
|
1298
|
+
|
|
1299
|
+
/**
|
|
1300
|
+
* Provides the length of the input array.
|
|
1301
|
+
*
|
|
1302
|
+
* @param array: The array to inspect
|
|
1303
|
+
* @returns The number of elements in the array
|
|
1304
|
+
*
|
|
1305
|
+
* @example
|
|
1306
|
+
* use Array.{ module Immutable }
|
|
1307
|
+
* assert Immutable.length(Immutable.fromList([1, 2, 3, 4, 5])) == 5
|
|
1308
|
+
*
|
|
1309
|
+
* @since v0.6.0
|
|
1310
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1311
|
+
*/
|
|
1312
|
+
provide let length = array => array.length
|
|
1313
|
+
|
|
1314
|
+
let tailIndex = length => {
|
|
1315
|
+
let shiftedRight = length >> branchingBits
|
|
1316
|
+
shiftedRight << branchingBits
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
let wrapNegativeIndex = (len, index) => {
|
|
1320
|
+
if (index >= 0) index else len + index
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
let arraySetCopy = (index, value, array) => {
|
|
1324
|
+
let copy = mutCopy(array)
|
|
1325
|
+
copy[index] = value
|
|
1326
|
+
copy
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// Appends 2 arrays together, truncating to a max size of 32 for storage in
|
|
1330
|
+
// a tree node. Also returns number of elements truncated.
|
|
1331
|
+
let arrayAppendMax32 = (array1, array2) => {
|
|
1332
|
+
let len1 = mutLength(array1)
|
|
1333
|
+
let len2 = mutLength(array2)
|
|
1334
|
+
|
|
1335
|
+
let numToAppend = Number.min(len2, branchingFactor - len1)
|
|
1336
|
+
let toAppend = if (numToAppend < len2) {
|
|
1337
|
+
mutSlice(0, end=numToAppend, array2)
|
|
1338
|
+
} else {
|
|
1339
|
+
array2
|
|
1340
|
+
}
|
|
1341
|
+
let numNotAppended = len1 + len2 - branchingFactor
|
|
1342
|
+
|
|
1343
|
+
(mutAppend(array1, toAppend), numNotAppended)
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
let log32floor = num => {
|
|
1347
|
+
let mut val = -1
|
|
1348
|
+
let mut num = num
|
|
1349
|
+
while (num > 0) {
|
|
1350
|
+
val += 1
|
|
1351
|
+
num = num >> branchingBits
|
|
1352
|
+
}
|
|
1353
|
+
val
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
/**
|
|
1357
|
+
* Retrieves the element from the array at the specified index.
|
|
1358
|
+
* A negative index is treated as an offset from the end of the array.
|
|
1359
|
+
*
|
|
1360
|
+
* @param index: The index to access
|
|
1361
|
+
* @param array: The array to access
|
|
1362
|
+
* @returns The element from the array
|
|
1363
|
+
*
|
|
1364
|
+
* @throws IndexOutOfBounds: When the index being accessed is outside the array's bounds
|
|
1365
|
+
*
|
|
1366
|
+
* @example
|
|
1367
|
+
* use Array.{ module Immutable }
|
|
1368
|
+
* assert Immutable.get(1, Immutable.fromList([1, 2, 3, 4])) == 2
|
|
1369
|
+
* @example
|
|
1370
|
+
* use Array.{ module Immutable }
|
|
1371
|
+
* assert Immutable.get(-1, Immutable.fromList([1, 2, 3, 4])) == 4
|
|
1372
|
+
*
|
|
1373
|
+
* @since v0.6.0
|
|
1374
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1375
|
+
*/
|
|
1376
|
+
provide let get = (index, array) => {
|
|
1377
|
+
let index = wrapNegativeIndex(array.length, index)
|
|
1378
|
+
|
|
1379
|
+
let rec getInner = (shift, node) => {
|
|
1380
|
+
let pos = index >> shift & bitmask
|
|
1381
|
+
match (node[pos]) {
|
|
1382
|
+
Tree(subTree) => getInner(shift - branchingBits, subTree),
|
|
1383
|
+
Leaf(vals) => vals[index & bitmask],
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
let { length, shift, root, tail } = array
|
|
1388
|
+
if (index < 0 || index >= length) {
|
|
1389
|
+
throw IndexOutOfBounds
|
|
1390
|
+
} else if (index >= tailIndex(length)) {
|
|
1391
|
+
tail[index & bitmask]
|
|
1392
|
+
} else {
|
|
1393
|
+
getInner(shift, root)
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
/**
|
|
1398
|
+
* Creates a new array in which the element at the specified index is set to a
|
|
1399
|
+
* new value. A negative index is treated as an offset from the end of the array.
|
|
1400
|
+
*
|
|
1401
|
+
* @param index: The index to update
|
|
1402
|
+
* @param value: The value to store
|
|
1403
|
+
* @param array: The array to update
|
|
1404
|
+
* @returns A new array containing the new element at the given index
|
|
1405
|
+
*
|
|
1406
|
+
* @throws IndexOutOfBounds: When the index being updated is outside the array's bounds
|
|
1407
|
+
*
|
|
1408
|
+
* @example
|
|
1409
|
+
* use Array.{ module Immutable }
|
|
1410
|
+
* let arr = Immutable.fromList([1, 2, 3, 4, 5])
|
|
1411
|
+
* let array = Immutable.set(1, 9, arr)
|
|
1412
|
+
* assert arr == Immutable.fromList([1, 2, 3, 4, 5])
|
|
1413
|
+
* assert array == Immutable.fromList([1, 9, 3, 4, 5])
|
|
1414
|
+
*
|
|
1415
|
+
* @since v0.6.0
|
|
1416
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1417
|
+
*/
|
|
1418
|
+
provide let set = (index, value, array) => {
|
|
1419
|
+
let index = wrapNegativeIndex(array.length, index)
|
|
1420
|
+
|
|
1421
|
+
let rec setInner = (shift, node) => {
|
|
1422
|
+
let pos = index >> shift & bitmask
|
|
1423
|
+
let newVal = match (node[pos]) {
|
|
1424
|
+
Tree(subTree) => {
|
|
1425
|
+
Tree(setInner(shift - branchingBits, subTree))
|
|
1426
|
+
},
|
|
1427
|
+
Leaf(vals) => {
|
|
1428
|
+
Leaf(arraySetCopy(index & bitmask, value, vals))
|
|
1429
|
+
},
|
|
1430
|
+
}
|
|
1431
|
+
arraySetCopy(pos, newVal, node)
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
let { length, shift, root, tail } = array
|
|
1435
|
+
if (index < 0 || index >= length) {
|
|
1436
|
+
throw IndexOutOfBounds
|
|
1437
|
+
} else if (index >= tailIndex(length)) {
|
|
1438
|
+
{ length, shift, root, tail: arraySetCopy(index & bitmask, value, tail) }
|
|
1439
|
+
} else {
|
|
1440
|
+
{ length, shift, root: setInner(shift, root), tail }
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
// Inserts a new tail into the array. If the length of the tail equals the
|
|
1445
|
+
// branching factor, it is instead inserted into the main tree rather than
|
|
1446
|
+
// the tail
|
|
1447
|
+
let replaceTail = (newTail, array) => {
|
|
1448
|
+
let rec insertTailInTree = (shift, node) => {
|
|
1449
|
+
let pos = array.length >> shift & bitmask
|
|
1450
|
+
if (pos >= mutLength(node)) {
|
|
1451
|
+
let newElem = if (shift == branchingBits) {
|
|
1452
|
+
Leaf(newTail)
|
|
1453
|
+
} else {
|
|
1454
|
+
Tree(insertTailInTree(shift - branchingBits, [>]))
|
|
1455
|
+
}
|
|
1456
|
+
mutAppend(node, [> newElem])
|
|
1457
|
+
} else {
|
|
1458
|
+
let newSubTree = match (node[pos]) {
|
|
1459
|
+
Tree(subTree) => subTree,
|
|
1460
|
+
Leaf(_) => [> node[pos]],
|
|
1461
|
+
}
|
|
1462
|
+
let newNode = Tree(insertTailInTree(shift - branchingBits, newSubTree))
|
|
1463
|
+
arraySetCopy(pos, newNode, node)
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
let { length, shift, root, tail } = array
|
|
1468
|
+
let newArrayLen = length + (mutLength(newTail) - mutLength(tail))
|
|
1469
|
+
if (mutLength(newTail) == branchingFactor) {
|
|
1470
|
+
let overflow = newArrayLen >> branchingBits > 1 << shift
|
|
1471
|
+
if (overflow) {
|
|
1472
|
+
let newShift = shift + branchingBits
|
|
1473
|
+
let newRoot = insertTailInTree(newShift, [> Tree(root)])
|
|
1474
|
+
{ length: newArrayLen, shift: newShift, root: newRoot, tail: [>] }
|
|
1475
|
+
} else {
|
|
1476
|
+
let newRoot = insertTailInTree(shift, root)
|
|
1477
|
+
{ length: newArrayLen, shift, root: newRoot, tail: [>] }
|
|
1478
|
+
}
|
|
1479
|
+
} else {
|
|
1480
|
+
{ length: newArrayLen, shift, root, tail: newTail }
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
let appendTree = (toAppend, array) => {
|
|
1485
|
+
let (appended, numNotAppended) = arrayAppendMax32(array.tail, toAppend)
|
|
1486
|
+
let newArray = replaceTail(appended, array)
|
|
1487
|
+
if (numNotAppended > 0) {
|
|
1488
|
+
let appendLen = mutLength(toAppend)
|
|
1489
|
+
let newTail = mutSlice(
|
|
1490
|
+
appendLen - numNotAppended,
|
|
1491
|
+
end=appendLen,
|
|
1492
|
+
toAppend
|
|
1493
|
+
)
|
|
1494
|
+
replaceTail(newTail, newArray)
|
|
1495
|
+
} else {
|
|
1496
|
+
newArray
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
// Flatten an array into a builder
|
|
1501
|
+
let arrayToBuilder = array => {
|
|
1502
|
+
let rec reduceFn = (acc, node) => {
|
|
1503
|
+
match (node) {
|
|
1504
|
+
Tree(subTree) => mutReduce(reduceFn, acc, subTree),
|
|
1505
|
+
Leaf(_) => [node, ...acc],
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
let { tail, root, length, _ } = array
|
|
1509
|
+
{
|
|
1510
|
+
btail: tail,
|
|
1511
|
+
nodes: mutReduce(reduceFn, [], root),
|
|
1512
|
+
numNodes: length >> branchingBits,
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
// For use to compress a large (> 32) list of nodes into subtrees
|
|
1517
|
+
let rec compressNodes = (nodes, acc) => {
|
|
1518
|
+
let node = mutFromList(List.take(branchingFactor, nodes))
|
|
1519
|
+
let remaining = List.drop(branchingFactor, nodes)
|
|
1520
|
+
let newAcc = [Tree(node), ...acc]
|
|
1521
|
+
match (remaining) {
|
|
1522
|
+
[] => List.reverse(newAcc),
|
|
1523
|
+
_ => compressNodes(remaining, newAcc),
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
let builderToArray = builder => {
|
|
1528
|
+
// Builds the non-tail portion of an array
|
|
1529
|
+
let rec buildTree = (nodes, numNodes) => {
|
|
1530
|
+
let newNodeSize = Number.ceil(numNodes / branchingFactor)
|
|
1531
|
+
match (newNodeSize) {
|
|
1532
|
+
1 => mutFromList(nodes),
|
|
1533
|
+
_ => buildTree(compressNodes(nodes, []), newNodeSize),
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
let { btail, nodes, numNodes } = builder
|
|
1538
|
+
match (numNodes) {
|
|
1539
|
+
0 =>
|
|
1540
|
+
{
|
|
1541
|
+
length: mutLength(btail),
|
|
1542
|
+
shift: branchingBits,
|
|
1543
|
+
root: [>],
|
|
1544
|
+
tail: btail,
|
|
1545
|
+
},
|
|
1546
|
+
_ => {
|
|
1547
|
+
let treeSize = numNodes * branchingFactor
|
|
1548
|
+
let depth = log32floor(treeSize - 1)
|
|
1549
|
+
{
|
|
1550
|
+
length: treeSize + mutLength(btail),
|
|
1551
|
+
shift: Number.max(1, depth) * branchingBits,
|
|
1552
|
+
root: buildTree(nodes, numNodes),
|
|
1553
|
+
tail: btail,
|
|
1554
|
+
}
|
|
1555
|
+
},
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
// Append a chunk of <= 32 values to a builder
|
|
1560
|
+
let appendBuilder = (toAppend, builder) => {
|
|
1561
|
+
let { btail, nodes, numNodes } = builder
|
|
1562
|
+
let (appended, numNotAppended) = arrayAppendMax32(btail, toAppend)
|
|
1563
|
+
|
|
1564
|
+
if (numNotAppended >= 0) {
|
|
1565
|
+
let appendLen = mutLength(toAppend)
|
|
1566
|
+
{
|
|
1567
|
+
btail: mutSlice(appendLen - numNotAppended, end=appendLen, toAppend),
|
|
1568
|
+
nodes: [Leaf(appended), ...nodes],
|
|
1569
|
+
numNodes: numNodes + 1,
|
|
1570
|
+
}
|
|
1571
|
+
} else {
|
|
1572
|
+
{ btail: appended, nodes, numNodes }
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
/**
|
|
1577
|
+
* Creates a new array with the elements of the first array followed by
|
|
1578
|
+
* the elements of the second array.
|
|
1579
|
+
*
|
|
1580
|
+
* @param array1: The array containing elements to appear first
|
|
1581
|
+
* @param array2: The array containing elements to appear second
|
|
1582
|
+
* @returns The new array containing elements from `array1` followed by elements from `array2`
|
|
1583
|
+
*
|
|
1584
|
+
* @example
|
|
1585
|
+
* use Array.{ module Immutable }
|
|
1586
|
+
* let arr1 = Immutable.fromList([1, 2])
|
|
1587
|
+
* let arr2 = Immutable.fromList([3, 4, 5])
|
|
1588
|
+
* assert Immutable.append(arr1, arr2) == Immutable.fromList([1, 2, 3, 4, 5])
|
|
1589
|
+
* assert arr1 == Immutable.fromList([1, 2])
|
|
1590
|
+
* assert arr2 == Immutable.fromList([3, 4, 5])
|
|
1591
|
+
*
|
|
1592
|
+
* @since v0.6.0
|
|
1593
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1594
|
+
*/
|
|
1595
|
+
provide let append = (array1, array2) => {
|
|
1596
|
+
// Magic number of 4 determined best from benchmarks according to Elm's
|
|
1597
|
+
// Array implementation
|
|
1598
|
+
if (array2.length <= branchingFactor * 4) {
|
|
1599
|
+
let rec reduceFn = (array, node) => {
|
|
1600
|
+
match (node) {
|
|
1601
|
+
Tree(subTree) => mutReduce(reduceFn, array, subTree),
|
|
1602
|
+
Leaf(vals) => appendTree(vals, array),
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
let withoutTail = mutReduce(reduceFn, array1, array2.root)
|
|
1606
|
+
appendTree(array2.tail, withoutTail)
|
|
1607
|
+
} else {
|
|
1608
|
+
let rec reduceFn = (builder, node) => {
|
|
1609
|
+
match (node) {
|
|
1610
|
+
Tree(subTree) => mutReduce(reduceFn, builder, subTree),
|
|
1611
|
+
Leaf(vals) => appendBuilder(vals, builder),
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
let withoutTail = mutReduce(reduceFn, arrayToBuilder(array1), array2.root)
|
|
1615
|
+
let { btail, nodes, numNodes } = appendBuilder(array2.tail, withoutTail)
|
|
1616
|
+
builderToArray({ btail, nodes: List.reverse(nodes), numNodes })
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
/**
|
|
1621
|
+
* Creates a single array containing the elements of all arrays in the
|
|
1622
|
+
* provided list.
|
|
1623
|
+
*
|
|
1624
|
+
* @param arrays: A list containing all arrays to combine
|
|
1625
|
+
* @returns The new array
|
|
1626
|
+
*
|
|
1627
|
+
* @example
|
|
1628
|
+
* use Array.{ module Immutable }
|
|
1629
|
+
* let arr1 = Immutable.fromList([1, 2])
|
|
1630
|
+
* let arr2 = Immutable.fromList([3, 4])
|
|
1631
|
+
* let arr3 = Immutable.fromList([5, 6])
|
|
1632
|
+
* assert Immutable.concat([arr1, arr2, arr3]) == Immutable.fromList([1, 2, 3, 4, 5, 6])
|
|
1633
|
+
*
|
|
1634
|
+
* @since v0.6.0
|
|
1635
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1636
|
+
*/
|
|
1637
|
+
provide let concat = arrays => {
|
|
1638
|
+
List.reduce(append, empty, arrays)
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
/**
|
|
1642
|
+
* Creates a new array of the specified length where each element is
|
|
1643
|
+
* initialized with the result of an initializer function. The initializer
|
|
1644
|
+
* is called with the index of each array element.
|
|
1645
|
+
*
|
|
1646
|
+
* @param length: The length of the new array
|
|
1647
|
+
* @param fn: The initializer function to call with each index, where the value returned will be used to initialize the element
|
|
1648
|
+
* @returns The new array
|
|
1649
|
+
*
|
|
1650
|
+
* @example
|
|
1651
|
+
* use Array.{ module Immutable }
|
|
1652
|
+
* assert Immutable.init(5, i => i) == Immutable.fromList([0, 1, 2, 3, 4])
|
|
1653
|
+
* @example
|
|
1654
|
+
* use Array.{ module Immutable }
|
|
1655
|
+
* assert Immutable.init(5, i => i + 3) == Immutable.fromList([3, 4, 5, 6, 7])
|
|
1656
|
+
*
|
|
1657
|
+
* @since v0.6.0
|
|
1658
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1659
|
+
*/
|
|
1660
|
+
provide let init = (length, fn) => {
|
|
1661
|
+
let tailLen = length % branchingFactor
|
|
1662
|
+
let btail = mutInit(tailLen, i => fn(length - tailLen + i))
|
|
1663
|
+
|
|
1664
|
+
let rec initInner = (beginI, nodes) => {
|
|
1665
|
+
if (beginI < 0) {
|
|
1666
|
+
builderToArray({ btail, nodes, numNodes: length >> branchingBits })
|
|
1667
|
+
} else {
|
|
1668
|
+
let leaf = Leaf(mutInit(branchingFactor, i => fn(beginI + i)))
|
|
1669
|
+
initInner(beginI - branchingFactor, [leaf, ...nodes])
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
initInner(length - tailLen - branchingFactor, [])
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
/**
|
|
1676
|
+
* Creates a new array of the specified length with each element being
|
|
1677
|
+
* initialized with the given value.
|
|
1678
|
+
*
|
|
1679
|
+
* @param length: The length of the new array
|
|
1680
|
+
* @param value: The value to store at each index
|
|
1681
|
+
* @returns The new array
|
|
1682
|
+
*
|
|
1683
|
+
* @example
|
|
1684
|
+
* use Array.{ module Immutable }
|
|
1685
|
+
* assert Immutable.make(5, "🌾") == Immutable.fromList(["🌾", "🌾", "🌾", "🌾", "🌾"])
|
|
1686
|
+
*
|
|
1687
|
+
* @since v0.6.0
|
|
1688
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1689
|
+
*/
|
|
1690
|
+
provide let make = (length, value) => {
|
|
1691
|
+
init(length, (_) => value)
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
/**
|
|
1695
|
+
* Iterates an array, calling an iterator function on each element.
|
|
1696
|
+
*
|
|
1697
|
+
* @param fn: The iterator function to call with each element
|
|
1698
|
+
* @param array: The array to iterate
|
|
1699
|
+
*
|
|
1700
|
+
* @example
|
|
1701
|
+
* use Array.{ module Immutable }
|
|
1702
|
+
* let arr = Immutable.fromList(["foo", "bar", "baz"])
|
|
1703
|
+
* let mut str = ""
|
|
1704
|
+
* Immutable.forEach(e => str = str ++ e, arr)
|
|
1705
|
+
* assert str == "foobarbaz"
|
|
1706
|
+
*
|
|
1707
|
+
* @since v0.6.0
|
|
1708
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1709
|
+
*/
|
|
1710
|
+
provide let forEach = (fn, array) => {
|
|
1711
|
+
let rec forEachFn = node => {
|
|
1712
|
+
match (node) {
|
|
1713
|
+
Tree(subTree) => mutForEach(forEachFn, subTree),
|
|
1714
|
+
Leaf(vals) => mutForEach(fn, vals),
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
let { tail, root, _ } = array
|
|
1718
|
+
mutForEach(forEachFn, root)
|
|
1719
|
+
mutForEach(fn, tail)
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
/**
|
|
1723
|
+
* Iterates an array a given number of times, calling an iterator function on each element.
|
|
1724
|
+
*
|
|
1725
|
+
* @param fn: The iterator function to call with each element
|
|
1726
|
+
* @param n: The number of times to iterate the given array
|
|
1727
|
+
* @param array: The array to iterate
|
|
1728
|
+
*
|
|
1729
|
+
* @example
|
|
1730
|
+
* use Array.{ module Immutable }
|
|
1731
|
+
* let arr = Immutable.fromList(["a", "b", "c"])
|
|
1732
|
+
* let mut str = ""
|
|
1733
|
+
* Immutable.cycle(e => str = str ++ e, 2, arr)
|
|
1734
|
+
* assert str == "abcabc"
|
|
1735
|
+
*
|
|
1736
|
+
* @since v0.6.0
|
|
1737
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1738
|
+
*/
|
|
1739
|
+
provide let cycle = (fn, n, array) => {
|
|
1740
|
+
for (let mut i = 0; i < n; i += 1) {
|
|
1741
|
+
forEach(fn, array)
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
/**
|
|
1746
|
+
* Produces a new array initialized with the results of a mapper function
|
|
1747
|
+
* called on each element of the input array.
|
|
1748
|
+
*
|
|
1749
|
+
* @param fn: The mapper function to call on each element, where the value returned will be used to initialize the element in the new array
|
|
1750
|
+
* @param array: The array to iterate
|
|
1751
|
+
* @returns The new array with mapped values
|
|
1752
|
+
*
|
|
1753
|
+
* @example
|
|
1754
|
+
* use Array.{ module Immutable }
|
|
1755
|
+
* let arr = Immutable.fromList(["foo", "bar", "baz"])
|
|
1756
|
+
* let arr = Immutable.map(e => e ++ "_", arr)
|
|
1757
|
+
* assert arr == Immutable.fromList(["foo_", "bar_", "baz_"])
|
|
1758
|
+
*
|
|
1759
|
+
* @since v0.6.0
|
|
1760
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1761
|
+
*/
|
|
1762
|
+
provide let map = (fn, array) => {
|
|
1763
|
+
let rec mapFn = node => {
|
|
1764
|
+
match (node) {
|
|
1765
|
+
Tree(subTree) => Tree(mutMap(mapFn, subTree)),
|
|
1766
|
+
Leaf(vals) => Leaf(mutMap(fn, vals)),
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
let { length, shift, root, tail } = array
|
|
1770
|
+
let newRoot = mutMap(mapFn, root)
|
|
1771
|
+
let newTail = mutMap(fn, tail)
|
|
1772
|
+
{ length, shift, root: newRoot, tail: newTail }
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
/**
|
|
1776
|
+
* Combines all elements of an array using a reducer function,
|
|
1777
|
+
* starting from the "head", or left side, of the array.
|
|
1778
|
+
*
|
|
1779
|
+
* In `ImmutableArray.reduce(fn, initial, array)`, `fn` is called with
|
|
1780
|
+
* an accumulator and each element of the array, and returns
|
|
1781
|
+
* a new accumulator. The final value is the last accumulator
|
|
1782
|
+
* returned. The accumulator starts with value `initial`.
|
|
1783
|
+
*
|
|
1784
|
+
* @param fn: The reducer function to call on each element, where the value returned will be the next accumulator value
|
|
1785
|
+
* @param initial: The initial value to use for the accumulator on the first iteration
|
|
1786
|
+
* @param array: The array to iterate
|
|
1787
|
+
* @returns The final accumulator returned from `fn`
|
|
1788
|
+
*
|
|
1789
|
+
* @example
|
|
1790
|
+
* use Array.{ module Immutable }
|
|
1791
|
+
* let arr = Immutable.fromList([1, 2, 3])
|
|
1792
|
+
* assert Immutable.reduce((acc, x) => acc + x, 0, arr) == 6
|
|
1793
|
+
*
|
|
1794
|
+
* @since v0.6.0
|
|
1795
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1796
|
+
*/
|
|
1797
|
+
provide let reduce = (fn, initial, array) => {
|
|
1798
|
+
let rec reduceFn = (acc, node) => {
|
|
1799
|
+
match (node) {
|
|
1800
|
+
Tree(subTree) => mutReduce(reduceFn, acc, subTree),
|
|
1801
|
+
Leaf(vals) => mutReduce(fn, acc, vals),
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
let { tail, root, _ } = array
|
|
1805
|
+
let withoutTail = mutReduce(reduceFn, initial, root)
|
|
1806
|
+
mutReduce(fn, withoutTail, tail)
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
/**
|
|
1810
|
+
* Combines all elements of an array using a reducer function,
|
|
1811
|
+
* starting from the "end", or right side, of the array.
|
|
1812
|
+
*
|
|
1813
|
+
* In `ImmutableArray.reduceRight(fn, initial, array)`, `fn` is called with
|
|
1814
|
+
* each element of the array and an accumulator, and returns
|
|
1815
|
+
* a new accumulator. The final value is the last accumulator
|
|
1816
|
+
* returned. The accumulator starts with value `initial`.
|
|
1817
|
+
*
|
|
1818
|
+
* @param fn: The reducer function to call on each element, where the value returned will be the next accumulator value
|
|
1819
|
+
* @param initial: The initial value to use for the accumulator on the first iteration
|
|
1820
|
+
* @param array: The array to iterate
|
|
1821
|
+
* @returns The final accumulator returned from `fn`
|
|
1822
|
+
*
|
|
1823
|
+
* @example
|
|
1824
|
+
* use Array.{ module Immutable }
|
|
1825
|
+
* let arr = Immutable.fromList(["baz", "bar", "foo"])
|
|
1826
|
+
* assert Immutable.reduceRight((x, acc) => acc ++ x, "", arr) == "foobarbaz"
|
|
1827
|
+
*
|
|
1828
|
+
* @since v0.6.0
|
|
1829
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1830
|
+
*/
|
|
1831
|
+
provide let reduceRight = (fn, initial, array) => {
|
|
1832
|
+
let rec reduceFn = (node, acc) => {
|
|
1833
|
+
match (node) {
|
|
1834
|
+
Tree(subTree) => mutReduceRight(reduceFn, acc, subTree),
|
|
1835
|
+
Leaf(vals) => mutReduceRight(fn, acc, vals),
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
let { tail, root, _ } = array
|
|
1839
|
+
let tailVal = mutReduceRight(fn, initial, tail)
|
|
1840
|
+
mutReduceRight(reduceFn, tailVal, root)
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
/**
|
|
1844
|
+
* Produces a new array by calling a function on each element
|
|
1845
|
+
* of the input array. Each iteration produces an intermediate
|
|
1846
|
+
* array, which are all appended to produce a "flattened" array
|
|
1847
|
+
* of all results.
|
|
1848
|
+
*
|
|
1849
|
+
* @param fn: The function to be called on each element, where the value returned will be an array that gets appended to the new array
|
|
1850
|
+
* @param array: The array to iterate
|
|
1851
|
+
* @returns The new array
|
|
1852
|
+
*
|
|
1853
|
+
* @example
|
|
1854
|
+
* use Array.{ module Immutable }
|
|
1855
|
+
* let arr = Immutable.fromList([1, 3, 5])
|
|
1856
|
+
* let arr = Immutable.flatMap(n => Immutable.fromList([n, n + 1]), arr)
|
|
1857
|
+
* assert arr == Immutable.fromList([1, 2, 3, 4, 5, 6])
|
|
1858
|
+
*
|
|
1859
|
+
* @since v0.6.0
|
|
1860
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1861
|
+
*/
|
|
1862
|
+
provide let flatMap = (fn, array) => {
|
|
1863
|
+
reduce((acc, x) => append(acc, fn(x)), empty, array)
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
/**
|
|
1867
|
+
* Converts the input list to an array.
|
|
1868
|
+
*
|
|
1869
|
+
* @param list: The list to convert
|
|
1870
|
+
* @returns The array containing all elements from the list
|
|
1871
|
+
*
|
|
1872
|
+
* @example
|
|
1873
|
+
* use Array.{ module Immutable }
|
|
1874
|
+
* let arr = Immutable.fromList([1, 2, 3])
|
|
1875
|
+
* assert Immutable.get(1, arr) == 2
|
|
1876
|
+
*
|
|
1877
|
+
* @since v0.6.0
|
|
1878
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1879
|
+
*/
|
|
1880
|
+
provide let fromList = list => {
|
|
1881
|
+
let rec fromListInner = (list, nodes, numNodes) => {
|
|
1882
|
+
let node = mutFromList(List.take(branchingFactor, list))
|
|
1883
|
+
let remaining = List.drop(branchingFactor, list)
|
|
1884
|
+
if (mutLength(node) < branchingFactor) {
|
|
1885
|
+
builderToArray({ btail: node, nodes: List.reverse(nodes), numNodes })
|
|
1886
|
+
} else {
|
|
1887
|
+
fromListInner(remaining, [Leaf(node), ...nodes], numNodes + 1)
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
fromListInner(list, [], 0)
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
/**
|
|
1895
|
+
* Converts the input array to a list.
|
|
1896
|
+
*
|
|
1897
|
+
* @param array: The array to convert
|
|
1898
|
+
* @returns The list containing all elements from the array
|
|
1899
|
+
*
|
|
1900
|
+
* @example
|
|
1901
|
+
* use Array.{ module Immutable }
|
|
1902
|
+
* let arr = Immutable.fromList(['a', 'b', 'c'])
|
|
1903
|
+
* let arr = Immutable.set(0, 'd', arr)
|
|
1904
|
+
* assert Immutable.toList(arr) == ['d', 'b', 'c']
|
|
1905
|
+
*
|
|
1906
|
+
* @since v0.6.0
|
|
1907
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1908
|
+
*/
|
|
1909
|
+
provide let toList = array => {
|
|
1910
|
+
reduceRight((val, list) => [val, ...list], [], array)
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
/**
|
|
1914
|
+
* Produces a new array by calling a function on each element of
|
|
1915
|
+
* the input array and only including it in the result array if the element satisfies
|
|
1916
|
+
* the condition.
|
|
1917
|
+
*
|
|
1918
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
1919
|
+
* @param array: The array to iterate
|
|
1920
|
+
* @returns The new array containing elements where `fn` returned `true`
|
|
1921
|
+
*
|
|
1922
|
+
* @example
|
|
1923
|
+
* use Array.{ module Immutable }
|
|
1924
|
+
* let arr = Immutable.fromList(['a', 'a', 'b', 'c'])
|
|
1925
|
+
* let arr = Immutable.filter(e => e == 'a', arr)
|
|
1926
|
+
* assert Immutable.toList(arr) == ['a', 'a']
|
|
1927
|
+
*
|
|
1928
|
+
* @since v0.6.0
|
|
1929
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1930
|
+
*/
|
|
1931
|
+
provide let filter = (fn, array) => {
|
|
1932
|
+
fromList(
|
|
1933
|
+
reduceRight((x, arr) => if (fn(x)) [x, ...arr] else arr, [], array)
|
|
1934
|
+
)
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
/**
|
|
1938
|
+
* Checks that the given condition is satisfied for all
|
|
1939
|
+
* elements in the input array.
|
|
1940
|
+
*
|
|
1941
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
1942
|
+
* @param array: The array to check
|
|
1943
|
+
* @returns `true` if all elements satify the condition or `false` otherwise
|
|
1944
|
+
*
|
|
1945
|
+
* @example
|
|
1946
|
+
* use Array.{ module Immutable }
|
|
1947
|
+
* let arr = Immutable.fromList(['a', 'a'])
|
|
1948
|
+
* assert Immutable.every(e => e == 'a', arr) == true
|
|
1949
|
+
* @example
|
|
1950
|
+
* use Array.{ module Immutable }
|
|
1951
|
+
* let arr = Immutable.fromList(['a', 'a', 'b', 'c'])
|
|
1952
|
+
* assert Immutable.every(e => e == 'a', arr) == false
|
|
1953
|
+
*
|
|
1954
|
+
* @since v0.6.0
|
|
1955
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1956
|
+
*/
|
|
1957
|
+
provide let every = (fn, array) => {
|
|
1958
|
+
reduce((acc, x) => acc && fn(x), true, array)
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
/**
|
|
1962
|
+
* Checks that the given condition is satisfied **at least
|
|
1963
|
+
* once** by an element in the input array.
|
|
1964
|
+
*
|
|
1965
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
1966
|
+
* @param array: The array to iterate
|
|
1967
|
+
* @returns `true` if one or more elements satisfy the condition or `false` otherwise
|
|
1968
|
+
*
|
|
1969
|
+
* @example
|
|
1970
|
+
* use Array.{ module Immutable }
|
|
1971
|
+
* let arr = Immutable.fromList(['a', 'a', 'b', 'c'])
|
|
1972
|
+
* assert Immutable.every(e => e == 'a', arr) == false
|
|
1973
|
+
* @example
|
|
1974
|
+
* use Array.{ module Immutable }
|
|
1975
|
+
* let arr = Immutable.fromList(['b', 'c'])
|
|
1976
|
+
* assert Immutable.some(e => e == 'a', arr) == false
|
|
1977
|
+
*
|
|
1978
|
+
* @since v0.6.0
|
|
1979
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1980
|
+
*/
|
|
1981
|
+
provide let some = (fn, array) => {
|
|
1982
|
+
reduce((acc, x) => acc || fn(x), false, array)
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
/**
|
|
1986
|
+
* Creates a new array with all elements in reverse order.
|
|
1987
|
+
*
|
|
1988
|
+
* @param array: The array to reverse
|
|
1989
|
+
* @returns The new array
|
|
1990
|
+
*
|
|
1991
|
+
* @example
|
|
1992
|
+
* use Array.{ module Immutable }
|
|
1993
|
+
* let arr = Immutable.fromList(['a', 'b', 'c'])
|
|
1994
|
+
* let arr = Immutable.reverse(arr)
|
|
1995
|
+
* assert Immutable.toList(arr) == ['c', 'b', 'a']
|
|
1996
|
+
*
|
|
1997
|
+
* @since v0.6.0
|
|
1998
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
1999
|
+
*/
|
|
2000
|
+
provide let reverse = array => {
|
|
2001
|
+
fromList(reduce((acc, x) => [x, ...acc], [], array))
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
/**
|
|
2005
|
+
* Checks if the value is an element of the input array.
|
|
2006
|
+
* Uses the generic `==` structural equality operator.
|
|
2007
|
+
*
|
|
2008
|
+
* @param search: The value to compare
|
|
2009
|
+
* @param array: The array to inspect
|
|
2010
|
+
* @returns `true` if the value exists in the array or `false` otherwise
|
|
2011
|
+
*
|
|
2012
|
+
* @example
|
|
2013
|
+
* use Array.{ module Immutable }
|
|
2014
|
+
* let arr = Immutable.fromList(['a', 'b', 'c'])
|
|
2015
|
+
* assert Immutable.contains('a', arr) == true
|
|
2016
|
+
* @example
|
|
2017
|
+
* use Array.{ module Immutable }
|
|
2018
|
+
* let arr = Immutable.fromList(['b', 'c'])
|
|
2019
|
+
* assert Immutable.contains('a', arr) == false
|
|
2020
|
+
*
|
|
2021
|
+
* @since v0.6.0
|
|
2022
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
2023
|
+
*/
|
|
2024
|
+
provide let contains = (search, array) => {
|
|
2025
|
+
reduce((acc, x) => acc || x == search, false, array)
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
/**
|
|
2029
|
+
* Finds the first element in an array that satisfies the given condition.
|
|
2030
|
+
*
|
|
2031
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
2032
|
+
* @param array: The array to search
|
|
2033
|
+
* @returns `Some(element)` containing the first value found or `None` otherwise
|
|
2034
|
+
*
|
|
2035
|
+
* @example
|
|
2036
|
+
* use Array.{ module Immutable }
|
|
2037
|
+
* let arr = Immutable.fromList([1, 2, 3])
|
|
2038
|
+
* assert Immutable.find(e => e == 2, arr) == Some(2)
|
|
2039
|
+
* @example
|
|
2040
|
+
* use Array.{ module Immutable }
|
|
2041
|
+
* let arr = Immutable.fromList([1, 3])
|
|
2042
|
+
* assert Immutable.find(e => e == 2, arr) == None
|
|
2043
|
+
*
|
|
2044
|
+
* @since v0.6.0
|
|
2045
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
2046
|
+
*/
|
|
2047
|
+
provide let find = (fn, array) => {
|
|
2048
|
+
reduce((acc, x) => if (acc == None && fn(x)) Some(x) else acc, None, array)
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
/**
|
|
2052
|
+
* Finds the first index in an array where the element satisfies the given condition.
|
|
2053
|
+
*
|
|
2054
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
2055
|
+
* @param array: The array to search
|
|
2056
|
+
* @returns `Some(index)` containing the index of the first element found or `None` otherwise
|
|
2057
|
+
*
|
|
2058
|
+
* @example
|
|
2059
|
+
* use Array.{ module Immutable }
|
|
2060
|
+
* let arr = Immutable.fromList([1, 2, 3])
|
|
2061
|
+
* assert Immutable.findIndex(e => e == 2, arr) == Some(1)
|
|
2062
|
+
* @example
|
|
2063
|
+
* use Array.{ module Immutable }
|
|
2064
|
+
* let arr = Immutable.fromList([1, 3])
|
|
2065
|
+
* assert Immutable.findIndex(e => e == 2, arr) == None
|
|
2066
|
+
*
|
|
2067
|
+
* @since v0.6.0
|
|
2068
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
2069
|
+
*/
|
|
2070
|
+
provide let findIndex = (fn, array) => {
|
|
2071
|
+
let mut i = -1
|
|
2072
|
+
reduce((acc, x) => {
|
|
2073
|
+
i += 1
|
|
2074
|
+
if (acc == None && fn(x)) Some(i) else acc
|
|
2075
|
+
}, None, array)
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
/**
|
|
2079
|
+
* Combines two arrays into a Cartesian product of tuples containing
|
|
2080
|
+
* all ordered pairs `(a, b)`.
|
|
2081
|
+
*
|
|
2082
|
+
* @param array1: The array to provide values for the first tuple element
|
|
2083
|
+
* @param array2: The array to provide values for the second tuple element
|
|
2084
|
+
* @returns The new array containing all pairs of `(a, b)`
|
|
2085
|
+
*
|
|
2086
|
+
* @example
|
|
2087
|
+
* use Array.{ module Immutable }
|
|
2088
|
+
* let arr1 = Immutable.fromList([1, 2])
|
|
2089
|
+
* let arr2 = Immutable.fromList([3, 4])
|
|
2090
|
+
* assert Immutable.product(arr1, arr2) == Immutable.fromList([(1, 3), (1, 4), (2, 3), (2, 4)])
|
|
2091
|
+
*
|
|
2092
|
+
* @since v0.6.0
|
|
2093
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
2094
|
+
*/
|
|
2095
|
+
provide let product = (array1, array2) => {
|
|
2096
|
+
fromList(reduceRight((x1, list) => {
|
|
2097
|
+
reduceRight((x2, list) => [(x1, x2), ...list], list, array2)
|
|
2098
|
+
}, [], array1))
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
/**
|
|
2102
|
+
* Counts the number of elements in an array that satisfy the given condition.
|
|
2103
|
+
*
|
|
2104
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
2105
|
+
* @param array: The array to iterate
|
|
2106
|
+
* @returns The total number of elements that satisfy the condition
|
|
2107
|
+
*
|
|
2108
|
+
* @example
|
|
2109
|
+
* use Array.{ module Immutable }
|
|
2110
|
+
* let arr = Immutable.fromList([1, 1, 2, 3, 4])
|
|
2111
|
+
* assert Immutable.count(e => e == 1, arr) == 2
|
|
2112
|
+
*
|
|
2113
|
+
* @since v0.6.0
|
|
2114
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
2115
|
+
*/
|
|
2116
|
+
provide let count = (fn, array) => {
|
|
2117
|
+
reduce((acc, x) => if (fn(x)) acc + 1 else acc, 0, array)
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
/**
|
|
2121
|
+
* Produces a new array with any duplicates removed.
|
|
2122
|
+
* Uses the generic `==` structural equality operator.
|
|
2123
|
+
*
|
|
2124
|
+
* @param array: The array to filter
|
|
2125
|
+
* @returns The new array with only unique values
|
|
2126
|
+
*
|
|
2127
|
+
* @example
|
|
2128
|
+
* use Array.{ module Immutable }
|
|
2129
|
+
* let arr = Immutable.fromList([1, 1, 2, 3, 2, 4])
|
|
2130
|
+
* assert Immutable.unique(arr) == Immutable.fromList([1, 2, 3, 4])
|
|
2131
|
+
*
|
|
2132
|
+
* @since v0.6.0
|
|
2133
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
2134
|
+
*/
|
|
2135
|
+
provide let unique = array => {
|
|
2136
|
+
// TODO(#1651): improve performance
|
|
2137
|
+
fromList(List.unique(toList(array)))
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
/**
|
|
2141
|
+
* Produces a new array filled with tuples of elements from both given arrays.
|
|
2142
|
+
* The first tuple will contain the first item of each array, the second tuple
|
|
2143
|
+
* will contain the second item of each array, and so on.
|
|
2144
|
+
*
|
|
2145
|
+
* Calling this function with arrays of different sizes will cause the returned
|
|
2146
|
+
* array to have the length of the smaller array.
|
|
2147
|
+
*
|
|
2148
|
+
* @param array1: The array to provide values for the first tuple element
|
|
2149
|
+
* @param array2: The array to provide values for the second tuple element
|
|
2150
|
+
* @returns The new array containing indexed pairs of `(a, b)`
|
|
2151
|
+
*
|
|
2152
|
+
* @example
|
|
2153
|
+
* use Array.{ module Immutable }
|
|
2154
|
+
* let arr1 = Immutable.fromList([1, 2, 3])
|
|
2155
|
+
* let arr2 = Immutable.fromList([4, 5, 6])
|
|
2156
|
+
* assert Immutable.zip(arr1, arr2) == Immutable.fromList([(1, 4), (2, 5), (3, 6)])
|
|
2157
|
+
*
|
|
2158
|
+
* @since v0.6.0
|
|
2159
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
2160
|
+
*/
|
|
2161
|
+
provide let zip = (array1, array2) => {
|
|
2162
|
+
fromList(List.zip(toList(array1), toList(array2)))
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
/**
|
|
2166
|
+
* Produces a new array filled with elements defined by applying a function on
|
|
2167
|
+
* pairs from both given arrays. The first element will contain the result of
|
|
2168
|
+
* applying the function to the first elements of each array, the second element
|
|
2169
|
+
* will contain the result of applying the function to the second elements of
|
|
2170
|
+
* each array, and so on.
|
|
2171
|
+
*
|
|
2172
|
+
* Calling this function with arrays of different sizes will cause the returned
|
|
2173
|
+
* array to have the length of the smaller array.
|
|
2174
|
+
*
|
|
2175
|
+
* @param fn: The function to apply to pairs of elements
|
|
2176
|
+
* @param array1: The array whose elements will each be passed to the function as the first argument
|
|
2177
|
+
* @param array2: The array whose elements will each be passed to the function as the second argument
|
|
2178
|
+
* @returns The new array containing elements derived from applying the function to pairs of input array elements
|
|
2179
|
+
*
|
|
2180
|
+
* @example
|
|
2181
|
+
* use Array.{ module Immutable }
|
|
2182
|
+
* let arr1 = Immutable.fromList([1, 2, 3])
|
|
2183
|
+
* let arr2 = Immutable.fromList([4, 5, 6])
|
|
2184
|
+
* assert Immutable.zipWith((a, b) => a + b, arr1, arr2) == Immutable.fromList([5, 7, 9])
|
|
2185
|
+
* @example
|
|
2186
|
+
* use Array.{ module Immutable }
|
|
2187
|
+
* let arr1 = Immutable.fromList([1, 2, 3])
|
|
2188
|
+
* let arr2 = Immutable.fromList([4, 5, 6])
|
|
2189
|
+
* assert Immutable.zipWith((a, b) => a * b, arr1, arr2) == Immutable.fromList([4, 10, 18])
|
|
2190
|
+
*
|
|
2191
|
+
* @since v0.6.0
|
|
2192
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
2193
|
+
*/
|
|
2194
|
+
provide let zipWith = (fn, array1, array2) => {
|
|
2195
|
+
fromList(List.zipWith(fn, toList(array1), toList(array2)))
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
/**
|
|
2199
|
+
* Produces two arrays by splitting apart an array of tuples.
|
|
2200
|
+
*
|
|
2201
|
+
* @param array: The array of tuples to split
|
|
2202
|
+
* @returns An array containing all elements from the first tuple element and an array containing all elements from the second tuple element
|
|
2203
|
+
*
|
|
2204
|
+
* @example
|
|
2205
|
+
* use Array.{ module Immutable }
|
|
2206
|
+
* let arr1 = Immutable.fromList([(1, 2), (3, 4), (5, 6)])
|
|
2207
|
+
* let arr2 = Immutable.fromList([1, 3, 5])
|
|
2208
|
+
* let arr3 = Immutable.fromList([2, 4, 6])
|
|
2209
|
+
* assert Immutable.unzip(arr1) == (arr2, arr3)
|
|
2210
|
+
*
|
|
2211
|
+
* @since v0.6.0
|
|
2212
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
2213
|
+
*/
|
|
2214
|
+
provide let unzip = array => {
|
|
2215
|
+
let (list1, list2) = List.unzip(toList(array))
|
|
2216
|
+
(fromList(list1), fromList(list2))
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
/**
|
|
2220
|
+
* Concatenates an array of strings into a single string, separated by a separator string.
|
|
2221
|
+
*
|
|
2222
|
+
* @param separator: The separator to insert between items in the string
|
|
2223
|
+
* @param array: The input strings
|
|
2224
|
+
* @returns The concatenated string
|
|
2225
|
+
*
|
|
2226
|
+
* @example
|
|
2227
|
+
* use Array.{ module Immutable }
|
|
2228
|
+
* let arr = Immutable.fromList(["a", "b", "c"])
|
|
2229
|
+
* assert Immutable.join(", ", arr) == "a, b, c"
|
|
2230
|
+
*
|
|
2231
|
+
* @since v0.6.0
|
|
2232
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
2233
|
+
*/
|
|
2234
|
+
provide let join = (separator, array) => {
|
|
2235
|
+
// TODO(#728): Improve performance here with buffer approach
|
|
2236
|
+
let iter = (acc, str) => {
|
|
2237
|
+
match (acc) {
|
|
2238
|
+
None => Some(str),
|
|
2239
|
+
Some(prev) => Some(prev ++ separator ++ str),
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
match (reduce(iter, None, array)) {
|
|
2243
|
+
None => "",
|
|
2244
|
+
Some(s) => s,
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
let clampIndex = (len, index) => {
|
|
2249
|
+
Number.min(len, Number.max(0, wrapNegativeIndex(len, index)))
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
/**
|
|
2253
|
+
* Slices an array given zero-based start and end indexes. The value
|
|
2254
|
+
* at the end index will not be included in the result.
|
|
2255
|
+
*
|
|
2256
|
+
* If either index is a negative number, it will be treated as a reverse index from
|
|
2257
|
+
* the end of the array.
|
|
2258
|
+
*
|
|
2259
|
+
* @param start: The index of the array where the slice will begin (inclusive)
|
|
2260
|
+
* @param end: The index of the array where the slice will end (exclusive)
|
|
2261
|
+
* @param array: The array to be sliced
|
|
2262
|
+
* @returns The subset of the array that was sliced
|
|
2263
|
+
*
|
|
2264
|
+
* @example
|
|
2265
|
+
* use Array.{ module Immutable }
|
|
2266
|
+
* let arr = Immutable.fromList(['a', 'b', 'c'])
|
|
2267
|
+
* assert Immutable.slice(0, end=2, arr) == Immutable.fromList(['a', 'b'])
|
|
2268
|
+
* @example
|
|
2269
|
+
* use Array.{ module Immutable }
|
|
2270
|
+
* let arr = Immutable.fromList(['a', 'b', 'c'])
|
|
2271
|
+
* assert Immutable.slice(1, end=-1, arr) == Immutable.fromList(['b'])
|
|
2272
|
+
*
|
|
2273
|
+
* @since v0.6.0
|
|
2274
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
2275
|
+
* @history v0.6.0: Default `end` to the Array length
|
|
2276
|
+
*/
|
|
2277
|
+
provide let slice = (start, end=length(array), array) => {
|
|
2278
|
+
let begin = clampIndex(array.length, start)
|
|
2279
|
+
let end = clampIndex(array.length, end)
|
|
2280
|
+
let mut i = array.length
|
|
2281
|
+
fromList(reduceRight((x, acc) => {
|
|
2282
|
+
i -= 1
|
|
2283
|
+
if (i >= begin && i < end) [x, ...acc] else acc
|
|
2284
|
+
}, [], array))
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
/**
|
|
2288
|
+
* Sorts the given array based on a given comparator function.
|
|
2289
|
+
*
|
|
2290
|
+
* Ordering is calculated using a comparator function which takes two array elements and must return 0 if both are equal, a positive number if the first is greater, and a negative number if the first is smaller.
|
|
2291
|
+
* @param compare: The comparator function used to indicate sort order
|
|
2292
|
+
* @param array: The array to be sorted
|
|
2293
|
+
* @returns The sorted array
|
|
2294
|
+
*
|
|
2295
|
+
* @example
|
|
2296
|
+
* use Array.{ module Immutable }
|
|
2297
|
+
* let arr = Immutable.fromList([2, 3, 1, 4])
|
|
2298
|
+
* assert Immutable.sort(compare=(a, b) => a - b, arr) == Immutable.fromList([1, 2, 3, 4])
|
|
2299
|
+
*
|
|
2300
|
+
* @since v0.6.0
|
|
2301
|
+
* @history v0.5.4: Originally in `"immutablearray"` module, with `compare` being a required argument
|
|
2302
|
+
*/
|
|
2303
|
+
provide let sort = (compare=compare, array) => {
|
|
2304
|
+
fromList(List.sort(compare=compare, toList(array)))
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
/**
|
|
2308
|
+
* Rotates array elements by the specified amount to the left, such that the
|
|
2309
|
+
* `n`th element is the first in the new array.
|
|
2310
|
+
*
|
|
2311
|
+
* If value is negative, array elements will be rotated by the
|
|
2312
|
+
* specified amount to the right. See examples.
|
|
2313
|
+
*
|
|
2314
|
+
* @param n: The number of elements to rotate by
|
|
2315
|
+
* @param array: The array to be rotated
|
|
2316
|
+
*
|
|
2317
|
+
* @example
|
|
2318
|
+
* use Array.{ module Immutable }
|
|
2319
|
+
* let arr = Immutable.fromList([1, 2, 3, 4, 5])
|
|
2320
|
+
* assert Immutable.rotate(2, arr) == Immutable.fromList([3, 4, 5, 1, 2])
|
|
2321
|
+
* @example
|
|
2322
|
+
* use Array.{ module Immutable }
|
|
2323
|
+
* let arr = Immutable.fromList([1, 2, 3, 4, 5])
|
|
2324
|
+
* assert Immutable.rotate(-1, arr) == Immutable.fromList([5, 1, 2, 3, 4])
|
|
2325
|
+
*
|
|
2326
|
+
* @since v0.6.0
|
|
2327
|
+
* @history v0.5.4: Originally in `"immutablearray"` module
|
|
2328
|
+
*/
|
|
2329
|
+
provide let rotate = (n, array) => {
|
|
2330
|
+
let sliceI = if (array.length == 0) 0 else n % array.length
|
|
2331
|
+
let before = slice(0, end=sliceI, array)
|
|
2332
|
+
let after = slice(sliceI, end=array.length, array)
|
|
2333
|
+
append(after, before)
|
|
2334
|
+
}
|
|
2335
|
+
}
|