@grain/stdlib 0.4.4 → 0.5.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 +87 -0
- package/LICENSE +1 -1
- package/array.gr +92 -73
- package/array.md +18 -18
- package/bigint.gr +497 -0
- package/bigint.md +811 -0
- package/buffer.gr +56 -217
- package/buffer.md +24 -17
- package/bytes.gr +103 -205
- package/bytes.md +19 -0
- package/char.gr +152 -166
- package/char.md +200 -0
- package/exception.md +6 -0
- package/float32.gr +159 -82
- package/float32.md +315 -0
- package/float64.gr +163 -82
- package/float64.md +315 -0
- package/hash.gr +53 -49
- package/int32.gr +479 -230
- package/int32.md +937 -0
- package/int64.gr +479 -230
- package/int64.md +937 -0
- package/list.gr +530 -116
- package/list.md +1141 -0
- package/map.gr +302 -121
- package/map.md +525 -0
- package/number.gr +51 -57
- package/number.md +37 -3
- package/option.gr +25 -25
- package/option.md +1 -1
- package/package.json +3 -3
- package/pervasives.gr +504 -52
- package/pervasives.md +1116 -0
- package/queue.gr +8 -1
- package/queue.md +10 -0
- package/random.gr +196 -0
- package/random.md +179 -0
- package/range.gr +26 -26
- package/regex.gr +1833 -842
- package/regex.md +11 -11
- package/result.md +1 -1
- package/runtime/bigint.gr +2045 -0
- package/runtime/bigint.md +326 -0
- package/runtime/dataStructures.gr +99 -279
- package/runtime/dataStructures.md +391 -0
- package/runtime/debug.gr +0 -1
- package/runtime/debug.md +6 -0
- package/runtime/equal.gr +40 -37
- package/runtime/equal.md +6 -0
- package/runtime/exception.gr +28 -15
- package/runtime/exception.md +30 -0
- package/runtime/gc.gr +50 -20
- package/runtime/gc.md +36 -0
- package/runtime/malloc.gr +32 -22
- package/runtime/malloc.md +55 -0
- package/runtime/numberUtils.gr +297 -142
- package/runtime/numberUtils.md +54 -0
- package/runtime/numbers.gr +1204 -453
- package/runtime/numbers.md +300 -0
- package/runtime/string.gr +193 -228
- package/runtime/string.md +24 -0
- package/runtime/stringUtils.gr +62 -38
- package/runtime/stringUtils.md +6 -0
- package/runtime/unsafe/constants.gr +17 -0
- package/runtime/unsafe/constants.md +72 -0
- package/runtime/unsafe/conv.gr +10 -10
- package/runtime/unsafe/conv.md +71 -0
- package/runtime/unsafe/errors.md +204 -0
- package/runtime/unsafe/memory.gr +14 -3
- package/runtime/unsafe/memory.md +54 -0
- package/runtime/unsafe/printWasm.gr +4 -4
- package/runtime/unsafe/printWasm.md +24 -0
- package/runtime/unsafe/tags.gr +11 -10
- package/runtime/unsafe/tags.md +120 -0
- package/runtime/unsafe/wasmf32.gr +9 -2
- package/runtime/unsafe/wasmf32.md +168 -0
- package/runtime/unsafe/wasmf64.gr +9 -2
- package/runtime/unsafe/wasmf64.md +168 -0
- package/runtime/unsafe/wasmi32.gr +65 -47
- package/runtime/unsafe/wasmi32.md +282 -0
- package/runtime/unsafe/wasmi64.gr +78 -50
- package/runtime/unsafe/wasmi64.md +300 -0
- package/runtime/utils/printing.gr +62 -0
- package/runtime/utils/printing.md +18 -0
- package/runtime/wasi.gr +200 -46
- package/runtime/wasi.md +839 -0
- package/set.gr +125 -121
- package/set.md +24 -21
- package/stack.gr +29 -29
- package/stack.md +4 -6
- package/string.gr +434 -415
- package/string.md +3 -3
- package/sys/file.gr +477 -482
- package/sys/process.gr +33 -47
- package/sys/random.gr +48 -20
- package/sys/random.md +38 -0
- package/sys/time.gr +12 -28
package/list.gr
CHANGED
|
@@ -1,8 +1,31 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @module List: Utilities for working with lists.
|
|
3
|
+
*
|
|
4
|
+
* @example import List from "list"
|
|
5
|
+
*
|
|
6
|
+
* @since v0.2.0
|
|
7
|
+
* @history v0.1.0: Originally named `lists`
|
|
8
|
+
* @history v0.2.0: Renamed to `list`
|
|
9
|
+
*/
|
|
2
10
|
|
|
3
|
-
|
|
11
|
+
/**
|
|
12
|
+
* @section Values: Functions for working with the List data type.
|
|
13
|
+
*/
|
|
4
14
|
|
|
5
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new list of the specified length where each element is
|
|
17
|
+
* initialized with the result of an initializer function. The initializer
|
|
18
|
+
* is called with the index of each list element.
|
|
19
|
+
*
|
|
20
|
+
* @param length: The length of the new list
|
|
21
|
+
* @param fn: The initializer function to call with each index, where the value returned will be used to initialize the element
|
|
22
|
+
* @returns The new list
|
|
23
|
+
*
|
|
24
|
+
* @example List.init(5, n => n + 3) // [3, 4, 5, 6, 7]
|
|
25
|
+
*
|
|
26
|
+
* @since v0.3.0
|
|
27
|
+
*/
|
|
28
|
+
export let init = (length, fn) => {
|
|
6
29
|
// This method can be further improved by checking the length against a specific size
|
|
7
30
|
// and determining if it can be made tail-recursive, then use List.reverse on it.
|
|
8
31
|
// Which would be the same as OCaml does it in https://github.com/ocaml/ocaml/blob/03839754f46319aa36d9dad56940a6f3c3bcb48a/stdlib/list.ml#L79
|
|
@@ -16,204 +39,463 @@ let init = (length, fn) => {
|
|
|
16
39
|
iter(0, length)
|
|
17
40
|
}
|
|
18
41
|
|
|
19
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Computes the length of the input list.
|
|
44
|
+
*
|
|
45
|
+
* @param list: The list to inspect
|
|
46
|
+
* @returns The number of elements in the list
|
|
47
|
+
*
|
|
48
|
+
* @since v0.1.0
|
|
49
|
+
* @history v0.2.0: Made the function tail-recursive
|
|
50
|
+
*/
|
|
51
|
+
export let length = list => {
|
|
20
52
|
let rec iter = (len, list) => {
|
|
21
53
|
match (list) {
|
|
22
54
|
[] => len,
|
|
23
|
-
[_, ...rest] => iter(len + 1, rest)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
iter(0, list)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
let sum = list => {
|
|
30
|
-
let rec iter = (n, list) => {
|
|
31
|
-
match (list) {
|
|
32
|
-
[] => n,
|
|
33
|
-
[first, ...rest] => iter(n + first, rest)
|
|
55
|
+
[_, ...rest] => iter(len + 1, rest),
|
|
34
56
|
}
|
|
35
57
|
}
|
|
36
58
|
iter(0, list)
|
|
37
59
|
}
|
|
38
60
|
|
|
39
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Creates a new list with all elements in reverse order.
|
|
63
|
+
*
|
|
64
|
+
* @param list: The list to reverse
|
|
65
|
+
* @returns The new list
|
|
66
|
+
*
|
|
67
|
+
* @since v0.1.0
|
|
68
|
+
*/
|
|
69
|
+
export let reverse = list => {
|
|
40
70
|
let rec iter = (list, acc) => {
|
|
41
71
|
match (list) {
|
|
42
72
|
[] => acc,
|
|
43
|
-
[first, ...rest] => iter(rest, [first, ...acc])
|
|
73
|
+
[first, ...rest] => iter(rest, [first, ...acc]),
|
|
44
74
|
}
|
|
45
75
|
}
|
|
46
76
|
iter(list, [])
|
|
47
77
|
}
|
|
48
78
|
|
|
49
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Creates a new list with the elements of the first list followed by
|
|
81
|
+
* the elements of the second list.
|
|
82
|
+
*
|
|
83
|
+
* @param list1: The list containing elements to appear first
|
|
84
|
+
* @param list2: The list containing elements to appear second
|
|
85
|
+
* @returns The new list containing elements from `list1` followed by elements from `list2`
|
|
86
|
+
*
|
|
87
|
+
* @since v0.1.0
|
|
88
|
+
*/
|
|
89
|
+
export let rec append = (list1, list2) => {
|
|
50
90
|
match (list1) {
|
|
51
91
|
[] => list2,
|
|
52
|
-
[first, ...rest] => [first, ...append(rest, list2)]
|
|
92
|
+
[first, ...rest] => [first, ...append(rest, list2)],
|
|
53
93
|
}
|
|
54
94
|
}
|
|
55
95
|
|
|
56
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Checks if the value is an element of the input list.
|
|
98
|
+
* Uses the generic `==` structural equality operator.
|
|
99
|
+
*
|
|
100
|
+
* @param search: The value to compare
|
|
101
|
+
* @param list: The list to inspect
|
|
102
|
+
* @returns `true` if the value exists in the list or `false` otherwise
|
|
103
|
+
*
|
|
104
|
+
* @since v0.1.0
|
|
105
|
+
*/
|
|
106
|
+
export let rec contains = (search, list) => {
|
|
57
107
|
match (list) {
|
|
58
108
|
[] => false,
|
|
59
|
-
[first, ...rest] =>
|
|
109
|
+
[first, ...rest] => first == search || contains(search, rest),
|
|
60
110
|
}
|
|
61
111
|
}
|
|
62
112
|
|
|
63
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Combines all elements of a list using a reducer function,
|
|
115
|
+
* starting from the "head", or left side, of the list.
|
|
116
|
+
*
|
|
117
|
+
* In `List.reduce(fn, initial, list)`, `fn` is called with
|
|
118
|
+
* an accumulator and each element of the list, and returns
|
|
119
|
+
* a new accumulator. The final value is the last accumulator
|
|
120
|
+
* returned. The accumulator starts with value `initial`.
|
|
121
|
+
*
|
|
122
|
+
* @param fn: The reducer function to call on each element, where the value returned will be the next accumulator value
|
|
123
|
+
* @param initial: The initial value to use for the accumulator on the first iteration
|
|
124
|
+
* @param list: The list to iterate
|
|
125
|
+
* @returns The final accumulator returned from `fn`
|
|
126
|
+
*
|
|
127
|
+
* @example List.reduce((a, b) => a + b, 0, [1, 2, 3]) // 6
|
|
128
|
+
*
|
|
129
|
+
* @since v0.2.0
|
|
130
|
+
* @history v0.1.0: Originally named `foldLeft`
|
|
131
|
+
* @history v0.2.0: Renamed to `reduce`
|
|
132
|
+
*/
|
|
133
|
+
export let rec reduce = (fn, initial, list) => {
|
|
64
134
|
match (list) {
|
|
65
|
-
[] =>
|
|
66
|
-
[first, ...rest] => reduce(fn, fn(
|
|
135
|
+
[] => initial,
|
|
136
|
+
[first, ...rest] => reduce(fn, fn(initial, first), rest),
|
|
67
137
|
}
|
|
68
138
|
}
|
|
69
139
|
|
|
70
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Combines all elements of a list using a reducer function,
|
|
142
|
+
* starting from the "end", or right side, of the list.
|
|
143
|
+
*
|
|
144
|
+
* In `List.reduceRight(fn, initial, list)`, `fn` is called with
|
|
145
|
+
* each element of the list and an accumulator, and returns
|
|
146
|
+
* a new accumulator. The final value is the last accumulator
|
|
147
|
+
* returned. The accumulator starts with value `initial`.
|
|
148
|
+
*
|
|
149
|
+
* @param fn: The reducer function to call on each element, where the value returned will be the next accumulator value
|
|
150
|
+
* @param initial: The initial value to use for the accumulator on the first iteration
|
|
151
|
+
* @param list: The list to iterate
|
|
152
|
+
* @returns The final accumulator returned from `fn`
|
|
153
|
+
*
|
|
154
|
+
* @example List.reduceRight((a, b) => b ++ a, "", ["baz", "bar", "foo"]) // "foobarbaz"
|
|
155
|
+
*
|
|
156
|
+
* @since v0.2.0
|
|
157
|
+
* @history v0.1.0: Originally named `foldRight`
|
|
158
|
+
* @history v0.2.0: Renamed to `reduceRight`
|
|
159
|
+
*/
|
|
160
|
+
export let rec reduceRight = (fn, initial, list) => {
|
|
71
161
|
match (list) {
|
|
72
|
-
[] =>
|
|
73
|
-
[first, ...rest] => fn(first, reduceRight(fn,
|
|
162
|
+
[] => initial,
|
|
163
|
+
[first, ...rest] => fn(first, reduceRight(fn, initial, rest)),
|
|
74
164
|
}
|
|
75
165
|
}
|
|
76
166
|
|
|
77
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Produces a new list initialized with the results of a mapper function
|
|
169
|
+
* called on each element of the input list.
|
|
170
|
+
*
|
|
171
|
+
* @param fn: The mapper function to call on each element, where the value returned will be used to initialize the element in the new list
|
|
172
|
+
* @param list: The list to iterate
|
|
173
|
+
* @returns The new list with mapped values
|
|
174
|
+
*
|
|
175
|
+
* @since v0.1.0
|
|
176
|
+
*/
|
|
177
|
+
export let rec map = (fn, list) => {
|
|
78
178
|
match (list) {
|
|
79
179
|
[] => [],
|
|
80
|
-
[first, ...rest] => [fn(first), ...map(fn, rest)]
|
|
180
|
+
[first, ...rest] => [fn(first), ...map(fn, rest)],
|
|
81
181
|
}
|
|
82
182
|
}
|
|
83
183
|
|
|
84
|
-
|
|
184
|
+
/**
|
|
185
|
+
* Produces a new list initialized with the results of a mapper function
|
|
186
|
+
* called on each element of the input list and its index.
|
|
187
|
+
*
|
|
188
|
+
* @param fn: The mapper function to call on each element, where the value returned will be used to initialize the element in the new list
|
|
189
|
+
* @param list: The list to iterate
|
|
190
|
+
* @returns The new list with mapped values
|
|
191
|
+
*
|
|
192
|
+
* @since v0.1.0
|
|
193
|
+
*/
|
|
194
|
+
export let mapi = (fn, list) => {
|
|
85
195
|
let rec iter = (fn, list, index) => {
|
|
86
196
|
match (list) {
|
|
87
197
|
[] => [],
|
|
88
|
-
[first, ...rest] => [fn(first, index), ...iter(fn, rest, index + 1)]
|
|
198
|
+
[first, ...rest] => [fn(first, index), ...iter(fn, rest, index + 1)],
|
|
89
199
|
}
|
|
90
200
|
}
|
|
91
201
|
iter(fn, list, 0)
|
|
92
202
|
}
|
|
93
203
|
|
|
94
|
-
|
|
204
|
+
/**
|
|
205
|
+
* Produces a new list by calling a function on each element
|
|
206
|
+
* of the input list. Each iteration produces an intermediate
|
|
207
|
+
* list, which are all appended to produce a "flattened" list
|
|
208
|
+
* of all results.
|
|
209
|
+
*
|
|
210
|
+
* @param fn: The function to be called on each element, where the value returned will be a list that gets appended to the new list
|
|
211
|
+
* @param list: The list to iterate
|
|
212
|
+
* @returns The new list
|
|
213
|
+
*
|
|
214
|
+
* @since v0.2.0
|
|
215
|
+
*/
|
|
216
|
+
export let rec flatMap = (fn, list) => {
|
|
95
217
|
match (list) {
|
|
96
218
|
[] => [],
|
|
97
|
-
[first, ...rest] => append(fn(first), flatMap(fn, rest))
|
|
219
|
+
[first, ...rest] => append(fn(first), flatMap(fn, rest)),
|
|
98
220
|
}
|
|
99
221
|
}
|
|
100
222
|
|
|
101
|
-
|
|
102
|
-
|
|
223
|
+
/**
|
|
224
|
+
* Checks that the given condition is satisfied for all
|
|
225
|
+
* elements in the input list.
|
|
226
|
+
*
|
|
227
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
228
|
+
* @param list: The list to check
|
|
229
|
+
* @returns `true` if all elements satify the condition or `false` otherwise
|
|
230
|
+
*
|
|
231
|
+
* @since v0.1.0
|
|
232
|
+
*/
|
|
233
|
+
export let every = (fn, list) => {
|
|
234
|
+
reduce((acc, value) => {
|
|
235
|
+
acc && fn(value)
|
|
236
|
+
}, true, list)
|
|
103
237
|
}
|
|
104
238
|
|
|
105
|
-
|
|
106
|
-
|
|
239
|
+
/**
|
|
240
|
+
* Checks that the given condition is satisfied **at least
|
|
241
|
+
* once** by an element in the input list.
|
|
242
|
+
*
|
|
243
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
244
|
+
* @param list: The list to iterate
|
|
245
|
+
* @returns `true` if one or more elements satify the condition or `false` otherwise
|
|
246
|
+
*
|
|
247
|
+
* @since v0.1.0
|
|
248
|
+
*/
|
|
249
|
+
export let some = (fn, list) => {
|
|
250
|
+
reduce((acc, value) => {
|
|
251
|
+
acc || fn(value)
|
|
252
|
+
}, false, list)
|
|
107
253
|
}
|
|
108
254
|
|
|
109
|
-
|
|
255
|
+
/**
|
|
256
|
+
* Iterates a list, calling an iterator function on each element.
|
|
257
|
+
*
|
|
258
|
+
* @param fn: The iterator function to call with each element
|
|
259
|
+
* @param list: The list to iterate
|
|
260
|
+
*
|
|
261
|
+
* @since v0.1.0
|
|
262
|
+
*/
|
|
263
|
+
export let rec forEach = (fn, list) => {
|
|
110
264
|
match (list) {
|
|
111
265
|
[] => void,
|
|
112
266
|
[first, ...rest] => {
|
|
113
|
-
fn(first)
|
|
267
|
+
fn(first): Void
|
|
114
268
|
forEach(fn, rest)
|
|
115
|
-
}
|
|
269
|
+
},
|
|
116
270
|
}
|
|
117
271
|
}
|
|
118
272
|
|
|
119
|
-
|
|
273
|
+
/**
|
|
274
|
+
* Iterates a list, calling an iterator function on each element.
|
|
275
|
+
* Also passes the index as the second argument to the function.
|
|
276
|
+
*
|
|
277
|
+
* @param fn: The iterator function to call with each element
|
|
278
|
+
* @param list: The list to iterate
|
|
279
|
+
*
|
|
280
|
+
* @since v0.1.0
|
|
281
|
+
*/
|
|
282
|
+
export let forEachi = (fn, list) => {
|
|
120
283
|
let rec iter = (fn, list, index) => {
|
|
121
284
|
match (list) {
|
|
122
285
|
[] => void,
|
|
123
286
|
[first, ...rest] => {
|
|
124
|
-
fn(first, index)
|
|
287
|
+
fn(first, index): Void
|
|
125
288
|
iter(fn, rest, index + 1)
|
|
126
|
-
}
|
|
289
|
+
},
|
|
127
290
|
}
|
|
128
291
|
}
|
|
129
292
|
iter(fn, list, 0)
|
|
130
293
|
}
|
|
131
294
|
|
|
132
|
-
|
|
295
|
+
/**
|
|
296
|
+
* Produces a new list by calling a function on each element of
|
|
297
|
+
* the input list and only including it in the result list if the element satisfies
|
|
298
|
+
* the condition.
|
|
299
|
+
*
|
|
300
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
301
|
+
* @param list: The list to iterate
|
|
302
|
+
* @returns The new list containing elements where `fn` returned `true`
|
|
303
|
+
*
|
|
304
|
+
* @since v0.1.0
|
|
305
|
+
*/
|
|
306
|
+
export let rec filter = (fn, list) => {
|
|
133
307
|
match (list) {
|
|
134
308
|
[] => [],
|
|
135
|
-
[first, ...rest] =>
|
|
309
|
+
[first, ...rest] =>
|
|
310
|
+
if (fn(first)) [first, ...filter(fn, rest)] else filter(fn, rest),
|
|
136
311
|
}
|
|
137
312
|
}
|
|
138
313
|
|
|
139
|
-
|
|
314
|
+
/**
|
|
315
|
+
* Produces a new list by calling a function on each element of
|
|
316
|
+
* the input list and only including it in the result list if the element satisfies
|
|
317
|
+
* the condition. Also passes the index to the function.
|
|
318
|
+
*
|
|
319
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
320
|
+
* @param list: The list to iterate
|
|
321
|
+
* @returns The new list containing elements where `fn` returned `true`
|
|
322
|
+
*
|
|
323
|
+
* @since v0.3.0
|
|
324
|
+
*/
|
|
325
|
+
export let filteri = (fn, list) => {
|
|
140
326
|
let rec iter = (fn, list, index) => {
|
|
141
327
|
match (list) {
|
|
142
328
|
[] => [],
|
|
143
|
-
[first, ...rest] =>
|
|
329
|
+
[first, ...rest] =>
|
|
330
|
+
if (fn(first, index)) [first, ...iter(fn, rest, index + 1)]
|
|
331
|
+
else iter(fn, rest, index + 1),
|
|
144
332
|
}
|
|
145
333
|
}
|
|
146
334
|
iter(fn, list, 0)
|
|
147
335
|
}
|
|
148
336
|
|
|
149
|
-
|
|
337
|
+
/**
|
|
338
|
+
* Produces a new list by calling a function on each element of
|
|
339
|
+
* the input list and excluding it from the result list if the element satisfies
|
|
340
|
+
* the condition.
|
|
341
|
+
*
|
|
342
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
343
|
+
* @param list: The list to iterate
|
|
344
|
+
* @returns The new list containing elements where `fn` returned `false`
|
|
345
|
+
*
|
|
346
|
+
* @since v0.1.0
|
|
347
|
+
*/
|
|
348
|
+
export let rec reject = (fn, list) => {
|
|
150
349
|
match (list) {
|
|
151
350
|
[] => [],
|
|
152
|
-
[first, ...rest] =>
|
|
351
|
+
[first, ...rest] =>
|
|
352
|
+
if (!fn(first)) [first, ...reject(fn, rest)] else reject(fn, rest),
|
|
153
353
|
}
|
|
154
354
|
}
|
|
155
355
|
|
|
156
|
-
|
|
356
|
+
/**
|
|
357
|
+
* Provides `Some(element)` containing the first element, or "head", of
|
|
358
|
+
* the input list or `None` if the list is empty.
|
|
359
|
+
*
|
|
360
|
+
* @param list: The list to access
|
|
361
|
+
* @returns `Some(firstElement)` if the list has elements or `None` otherwise
|
|
362
|
+
*
|
|
363
|
+
* @since v0.2.0
|
|
364
|
+
* @history v0.1.0: Originally named `hd`
|
|
365
|
+
* @history v0.2.0: Renamed to `head`
|
|
366
|
+
* @history v0.3.0: Return type converted to `Option` type
|
|
367
|
+
*/
|
|
368
|
+
export let head = list => {
|
|
157
369
|
match (list) {
|
|
158
370
|
[] => None,
|
|
159
|
-
[first, ..._] => Some(first)
|
|
371
|
+
[first, ..._] => Some(first),
|
|
160
372
|
}
|
|
161
373
|
}
|
|
162
374
|
|
|
163
|
-
|
|
375
|
+
/**
|
|
376
|
+
* Provides `Some(tail)` containing all list items except the first element, or "tail", of
|
|
377
|
+
* the input list or `None` if the list is empty.
|
|
378
|
+
*
|
|
379
|
+
* @param list: The list to access
|
|
380
|
+
* @returns `Some(tail)` if the list has elements or `None` otherwise
|
|
381
|
+
*
|
|
382
|
+
* @since v0.2.0
|
|
383
|
+
* @history v0.1.0: Originally named `tl`
|
|
384
|
+
* @history v0.2.0: Renamed to `tail`
|
|
385
|
+
* @history v0.3.0: Return type converted to `Option` type
|
|
386
|
+
*/
|
|
387
|
+
export let tail = list => {
|
|
164
388
|
match (list) {
|
|
165
389
|
[] => None,
|
|
166
|
-
[_, ...rest] => Some(rest)
|
|
390
|
+
[_, ...rest] => Some(rest),
|
|
167
391
|
}
|
|
168
392
|
}
|
|
169
393
|
|
|
170
|
-
|
|
394
|
+
/**
|
|
395
|
+
* Provides `Some(element)` containing the element in the list at the specified index
|
|
396
|
+
* or `None` if the index is out-of-bounds or the list is empty.
|
|
397
|
+
*
|
|
398
|
+
* @param index: The index to access
|
|
399
|
+
* @param list: The list to access
|
|
400
|
+
* @returns `Some(element)` if the list contains an element at the index or `None` otherwise
|
|
401
|
+
*
|
|
402
|
+
* @since v0.1.0
|
|
403
|
+
* @history v0.1.0: Originally failed for index out-of-bounds or list empty
|
|
404
|
+
* @history v0.3.0: Return type converted to `Option` type
|
|
405
|
+
*/
|
|
406
|
+
export let rec nth = (index, list) => {
|
|
171
407
|
if (index < 0) {
|
|
172
408
|
None
|
|
173
409
|
} else {
|
|
174
410
|
match (list) {
|
|
175
411
|
[] => None,
|
|
176
|
-
[first, ...rest] => if (index == 0) Some(first) else nth(index - 1, rest)
|
|
412
|
+
[first, ...rest] => if (index == 0) Some(first) else nth(index - 1, rest),
|
|
177
413
|
}
|
|
178
414
|
}
|
|
179
415
|
}
|
|
180
416
|
|
|
181
|
-
|
|
417
|
+
/**
|
|
418
|
+
* Flattens nested lists.
|
|
419
|
+
*
|
|
420
|
+
* @param list: The list to flatten
|
|
421
|
+
* @returns A new list containing all nested list elements combined
|
|
422
|
+
*
|
|
423
|
+
* @example List.flatten([[1, 2], [3, 4]]) // [1, 2, 3, 4]
|
|
424
|
+
*
|
|
425
|
+
* @since v0.1.0
|
|
426
|
+
*/
|
|
427
|
+
export let rec flatten = list => {
|
|
182
428
|
match (list) {
|
|
183
429
|
[] => [],
|
|
184
|
-
[first, ...rest] => append(first, flatten(rest))
|
|
430
|
+
[first, ...rest] => append(first, flatten(rest)),
|
|
185
431
|
}
|
|
186
432
|
}
|
|
187
433
|
|
|
188
|
-
|
|
434
|
+
/**
|
|
435
|
+
* Inserts a new value into a list at the specified index.
|
|
436
|
+
* Fails if the index is out-of-bounds.
|
|
437
|
+
*
|
|
438
|
+
* @param value: The value to insert
|
|
439
|
+
* @param index: The index to update
|
|
440
|
+
* @param list: The list to update
|
|
441
|
+
* @returns The new list
|
|
442
|
+
*
|
|
443
|
+
* @since v0.1.0
|
|
444
|
+
*/
|
|
445
|
+
export let rec insert = (value, index, list) => {
|
|
189
446
|
if (index < 0) {
|
|
190
447
|
fail "insert index cannot be a negative number"
|
|
191
448
|
} else {
|
|
192
|
-
match(list) {
|
|
449
|
+
match (list) {
|
|
193
450
|
[] => if (index == 0) [value] else fail "insert index is out-of-bounds",
|
|
194
|
-
[first, ...rest] =>
|
|
451
|
+
[first, ...rest] =>
|
|
452
|
+
if (index == 0) [value, ...list]
|
|
453
|
+
else [first, ...insert(value, index - 1, rest)],
|
|
195
454
|
}
|
|
196
455
|
}
|
|
197
456
|
}
|
|
198
457
|
|
|
199
|
-
|
|
458
|
+
/**
|
|
459
|
+
* Counts the number of elements in a list that satisfy the given condition.
|
|
460
|
+
*
|
|
461
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
462
|
+
* @param list: The list to iterate
|
|
463
|
+
* @returns The total number of elements that satisfy the condition
|
|
464
|
+
*
|
|
465
|
+
* @since v0.1.0
|
|
466
|
+
* @history v0.2.0: Made the function tail-recursive
|
|
467
|
+
*/
|
|
468
|
+
export let count = (fn, list) => {
|
|
200
469
|
let rec iter = (n, list) => {
|
|
201
470
|
match (list) {
|
|
202
471
|
[] => n,
|
|
203
|
-
[first, ...rest] => if (fn(first)) iter(n + 1, rest) else iter(n, rest)
|
|
472
|
+
[first, ...rest] => if (fn(first)) iter(n + 1, rest) else iter(n, rest),
|
|
204
473
|
}
|
|
205
474
|
}
|
|
206
475
|
iter(0, list)
|
|
207
476
|
}
|
|
208
477
|
|
|
209
|
-
|
|
478
|
+
/**
|
|
479
|
+
* Split a list into two, with the first list containing the required number of elements.
|
|
480
|
+
* Fails if the input list doesn't contain at least the required amount of elements.
|
|
481
|
+
*
|
|
482
|
+
* @param count: The number of elements required
|
|
483
|
+
* @param list: The list to split
|
|
484
|
+
* @returns Two lists where the first contains exactly the required amount of elements and the second contains any remaining elements
|
|
485
|
+
*
|
|
486
|
+
* @since v0.1.0
|
|
487
|
+
*/
|
|
488
|
+
export let part = (count, list) => {
|
|
210
489
|
if (count < 0) {
|
|
211
490
|
fail "part count cannot be a negative number"
|
|
212
491
|
} else {
|
|
213
492
|
let rec iter = (list1, list2, count) => {
|
|
214
493
|
match (list2) {
|
|
215
|
-
[] =>
|
|
216
|
-
|
|
494
|
+
[] =>
|
|
495
|
+
if (count > 0) fail "part count is out-of-bounds" else (list1, list2),
|
|
496
|
+
[first, ...rest] =>
|
|
497
|
+
if (count > 0) iter([first, ...list1], rest, count - 1)
|
|
498
|
+
else (list1, list2),
|
|
217
499
|
}
|
|
218
500
|
}
|
|
219
501
|
let (pt1, pt2) = iter([], list, count)
|
|
@@ -221,110 +503,238 @@ let part = (count, list) => {
|
|
|
221
503
|
}
|
|
222
504
|
}
|
|
223
505
|
|
|
224
|
-
|
|
225
|
-
|
|
506
|
+
/**
|
|
507
|
+
* Rotates list elements by the specified amount to the left.
|
|
508
|
+
*
|
|
509
|
+
* If value is negative, list elements will be rotated by the
|
|
510
|
+
* specified amount to the right. See examples.
|
|
511
|
+
*
|
|
512
|
+
* Fails if the input list doesn't contain at least the required amount of elements.
|
|
513
|
+
*
|
|
514
|
+
* @param count: The number of elements to rotate by
|
|
515
|
+
* @param list: The list to be rotated
|
|
516
|
+
*
|
|
517
|
+
* @example List.rotate(2, [1, 2, 3, 4, 5]) // [3, 4, 5, 1, 2]
|
|
518
|
+
* @example List.rotate(-1, [1, 2, 3, 4, 5]) // [5, 1, 2, 3, 4]
|
|
519
|
+
*
|
|
520
|
+
* @since v0.1.0
|
|
521
|
+
*/
|
|
522
|
+
export let rotate = (count, list) => {
|
|
523
|
+
let (beginning, end) = if (count >= 0) part(count, list)
|
|
524
|
+
else part(length(list) + count, list)
|
|
226
525
|
append(end, beginning)
|
|
227
526
|
}
|
|
228
527
|
|
|
229
|
-
|
|
528
|
+
/**
|
|
529
|
+
* Produces a new list with any duplicates removed.
|
|
530
|
+
* Uses the generic `==` structural equality operator.
|
|
531
|
+
*
|
|
532
|
+
* @param list: The list to filter
|
|
533
|
+
* @returns The new list with only unique values
|
|
534
|
+
*
|
|
535
|
+
* @since v0.2.0
|
|
536
|
+
* @history v0.1.0: Originally named `uniq`
|
|
537
|
+
* @history v0.2.0: Renamed to `unique`
|
|
538
|
+
*/
|
|
539
|
+
export let unique = list => {
|
|
230
540
|
let rec iter = (list, acc) => {
|
|
231
541
|
match (list) {
|
|
232
542
|
[] => reverse(acc),
|
|
233
|
-
[first, ...rest] =>
|
|
543
|
+
[first, ...rest] =>
|
|
544
|
+
if (contains(first, acc)) iter(rest, acc)
|
|
545
|
+
else iter(rest, [first, ...acc]),
|
|
234
546
|
}
|
|
235
547
|
}
|
|
236
548
|
iter(list, [])
|
|
237
549
|
}
|
|
238
550
|
|
|
239
|
-
|
|
240
|
-
|
|
551
|
+
/**
|
|
552
|
+
* Produces a new list with the specified number of elements removed from
|
|
553
|
+
* the beginning of the input list.
|
|
554
|
+
*
|
|
555
|
+
* Fails if the specified amount is a negative number.
|
|
556
|
+
*
|
|
557
|
+
* @param count: The amount of elements to remove
|
|
558
|
+
* @param list: The input list
|
|
559
|
+
* @returns The new list without the dropped elements
|
|
560
|
+
*
|
|
561
|
+
* @since v0.2.0
|
|
562
|
+
*/
|
|
563
|
+
export let rec drop = (count, list) => {
|
|
564
|
+
if (count < 0) {
|
|
241
565
|
fail "number of items to drop cannot be a negative number"
|
|
242
566
|
} else {
|
|
243
|
-
match ((
|
|
244
|
-
(_,[]) => [],
|
|
567
|
+
match ((count, list)) {
|
|
568
|
+
(_, []) => [],
|
|
245
569
|
(n, _) when n == 0 => list,
|
|
246
|
-
(n, [first, ...rest]) => drop(n - 1, rest)
|
|
570
|
+
(n, [first, ...rest]) => drop(n - 1, rest),
|
|
247
571
|
}
|
|
248
572
|
}
|
|
249
573
|
}
|
|
250
574
|
|
|
251
|
-
|
|
575
|
+
/**
|
|
576
|
+
* Produces a new list with the elements removed from the beginning
|
|
577
|
+
* of the input list until they no longer satisfy the given condition.
|
|
578
|
+
* Stops when the predicate function returns `false`.
|
|
579
|
+
*
|
|
580
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
581
|
+
* @param list: The input list
|
|
582
|
+
* @returns The new list without the dropped elements
|
|
583
|
+
*
|
|
584
|
+
* @since v0.2.0
|
|
585
|
+
*/
|
|
586
|
+
export let rec dropWhile = (fn, list) => {
|
|
252
587
|
match (list) {
|
|
253
588
|
[] => list,
|
|
254
|
-
[first, ...rest] => if (fn(first)) dropWhile(fn, rest) else list
|
|
589
|
+
[first, ...rest] => if (fn(first)) dropWhile(fn, rest) else list,
|
|
255
590
|
}
|
|
256
591
|
}
|
|
257
592
|
|
|
258
|
-
|
|
259
|
-
|
|
593
|
+
/**
|
|
594
|
+
* Produces a new list with–at most—the specified amount elements from
|
|
595
|
+
* the beginning of the input list.
|
|
596
|
+
*
|
|
597
|
+
* Fails if the specified amount is a negative number.
|
|
598
|
+
*
|
|
599
|
+
* @param count: The amount of elements to keep
|
|
600
|
+
* @param list: The input list
|
|
601
|
+
* @returns The new list containing the taken elements
|
|
602
|
+
*
|
|
603
|
+
* @since v0.2.0
|
|
604
|
+
*/
|
|
605
|
+
export let rec take = (count, list) => {
|
|
606
|
+
if (count < 0) {
|
|
260
607
|
fail "number of items to take cannot be a negative number"
|
|
261
608
|
} else {
|
|
262
|
-
match ((
|
|
609
|
+
match ((count, list)) {
|
|
263
610
|
(_, []) => list,
|
|
264
611
|
(n, _) when n == 0 => [],
|
|
265
|
-
(n, [first, ...rest]) => [first, ...take(n-1, rest)]
|
|
612
|
+
(n, [first, ...rest]) => [first, ...take(n - 1, rest)],
|
|
266
613
|
}
|
|
267
614
|
}
|
|
268
615
|
}
|
|
269
616
|
|
|
270
|
-
|
|
617
|
+
/**
|
|
618
|
+
* Produces a new list with elements from the beginning of the input list
|
|
619
|
+
* as long as they satisfy the given condition.
|
|
620
|
+
* Stops when the predicate function returns `false`.
|
|
621
|
+
*
|
|
622
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
623
|
+
* @param list: The input list
|
|
624
|
+
* @returns The new list containing the taken elements
|
|
625
|
+
*
|
|
626
|
+
* @since v0.2.0
|
|
627
|
+
*/
|
|
628
|
+
export let rec takeWhile = (fn, list) => {
|
|
271
629
|
match (list) {
|
|
272
630
|
[] => [],
|
|
273
|
-
[first, ...rest] => if (
|
|
631
|
+
[first, ...rest] => if (fn(first)) [first, ...takeWhile(fn, rest)] else [],
|
|
274
632
|
}
|
|
275
633
|
}
|
|
276
634
|
|
|
277
|
-
|
|
635
|
+
/**
|
|
636
|
+
* Finds the first element in a list that satifies the given condition.
|
|
637
|
+
*
|
|
638
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
639
|
+
* @param list: The list to search
|
|
640
|
+
* @returns `Some(element)` containing the first value found or `None` otherwise
|
|
641
|
+
*
|
|
642
|
+
* @since v0.2.0
|
|
643
|
+
* @history v0.2.0: Originally failed if the list was empty
|
|
644
|
+
* @history v0.3.0: Return type converted to `Option` type
|
|
645
|
+
*/
|
|
646
|
+
export let rec find = (fn, list) => {
|
|
278
647
|
match (list) {
|
|
279
648
|
[] => None,
|
|
280
|
-
[first, ...rest] => if (fn(first)) Some(first) else find(fn, rest)
|
|
649
|
+
[first, ...rest] => if (fn(first)) Some(first) else find(fn, rest),
|
|
281
650
|
}
|
|
282
651
|
}
|
|
283
652
|
|
|
284
|
-
|
|
653
|
+
/**
|
|
654
|
+
* Finds the first index in a list where the element satifies the given condition.
|
|
655
|
+
*
|
|
656
|
+
* @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
|
|
657
|
+
* @param list: The list to search
|
|
658
|
+
* @returns `Some(index)` containing the index of the first element found or `None` otherwise
|
|
659
|
+
*
|
|
660
|
+
* @since v0.2.0
|
|
661
|
+
* @history v0.2.0: Originally failed if the list was empty
|
|
662
|
+
* @history v0.3.0: Return type converted to `Option` type
|
|
663
|
+
*/
|
|
664
|
+
export let findIndex = (fn, list) => {
|
|
285
665
|
let rec findItemIndex = (l, index) => {
|
|
286
666
|
match (l) {
|
|
287
667
|
[] => None,
|
|
288
|
-
[first, ...rest] =>
|
|
668
|
+
[first, ...rest] =>
|
|
669
|
+
if (fn(first)) Some(index) else findItemIndex(rest, index + 1),
|
|
289
670
|
}
|
|
290
671
|
}
|
|
291
672
|
findItemIndex(list, 0)
|
|
292
673
|
}
|
|
293
674
|
|
|
294
|
-
|
|
675
|
+
/**
|
|
676
|
+
* Combines two lists into a Cartesian product of tuples containing
|
|
677
|
+
* all ordered pairs `(a, b)`.
|
|
678
|
+
*
|
|
679
|
+
* @param list1: The list to provide values for the first tuple element
|
|
680
|
+
* @param list2: The list to provide values for the second tuple element
|
|
681
|
+
* @returns The new list containing all pairs of `(a, b)`
|
|
682
|
+
*
|
|
683
|
+
* @since v0.2.0
|
|
684
|
+
*/
|
|
685
|
+
export let product = (list1, list2) => {
|
|
295
686
|
let mut list = []
|
|
296
|
-
forEach(
|
|
297
|
-
forEach(
|
|
298
|
-
|
|
299
|
-
},
|
|
300
|
-
},
|
|
687
|
+
forEach(aItem => {
|
|
688
|
+
forEach(bItem => {
|
|
689
|
+
list = [(aItem, bItem), ...list]
|
|
690
|
+
}, list2)
|
|
691
|
+
}, list1)
|
|
301
692
|
reverse(list)
|
|
302
693
|
}
|
|
303
694
|
|
|
304
|
-
|
|
695
|
+
/**
|
|
696
|
+
* Provides the subset of a list given zero-based start index and amount of elements
|
|
697
|
+
* to include.
|
|
698
|
+
*
|
|
699
|
+
* Fails if the start index or amount of elements are negative numbers.
|
|
700
|
+
*
|
|
701
|
+
* @param start: The index of the list where the subset will begin (inclusive)
|
|
702
|
+
* @param length: The amount of elements to be included in the subset
|
|
703
|
+
* @param list: The input list
|
|
704
|
+
* @returns The subset of the list
|
|
705
|
+
*
|
|
706
|
+
* @since v0.2.0
|
|
707
|
+
*/
|
|
708
|
+
export let sub = (start, length, list) => {
|
|
305
709
|
take(length, drop(start, list))
|
|
306
710
|
}
|
|
307
711
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
712
|
+
/**
|
|
713
|
+
* Combine the given list of strings into one string with the specified
|
|
714
|
+
* separator inserted between each item.
|
|
715
|
+
*
|
|
716
|
+
* @param separator: The separator to insert between elements
|
|
717
|
+
* @param list: The list to combine
|
|
718
|
+
* @returns The combined elements with the separator between each
|
|
719
|
+
*
|
|
720
|
+
* @since v0.4.0
|
|
721
|
+
*/
|
|
722
|
+
export let join = (separator: String, list: List<String>) => {
|
|
313
723
|
let rec iter = (sep, acc, rem) => {
|
|
314
|
-
match(rem) {
|
|
724
|
+
match (rem) {
|
|
315
725
|
[] => acc,
|
|
316
726
|
[hd, ...tl] => {
|
|
317
|
-
let newAcc = match(acc) {
|
|
727
|
+
let newAcc = match (acc) {
|
|
318
728
|
None => Some(hd),
|
|
319
|
-
Some(s) => Some(hd ++ sep ++ s)
|
|
729
|
+
Some(s) => Some(hd ++ sep ++ s),
|
|
320
730
|
}
|
|
321
731
|
iter(sep, newAcc, tl)
|
|
322
|
-
}
|
|
732
|
+
},
|
|
323
733
|
}
|
|
324
734
|
}
|
|
325
735
|
|
|
326
736
|
// Reverse and reduce to take advantage of TCE
|
|
327
|
-
match(iter(separator, None, reverse(
|
|
737
|
+
match (iter(separator, None, reverse(list))) {
|
|
328
738
|
None => "",
|
|
329
739
|
Some(s) => s,
|
|
330
740
|
}
|
|
@@ -332,29 +742,33 @@ let join = (separator: String, items: List<String>) => {
|
|
|
332
742
|
|
|
333
743
|
/**
|
|
334
744
|
* Reverses the first list and appends the second list to the end.
|
|
335
|
-
*
|
|
745
|
+
*
|
|
336
746
|
* @param list1: The list to reverse
|
|
337
747
|
* @param list2: The list to append
|
|
748
|
+
* @returns The new list
|
|
749
|
+
*
|
|
338
750
|
* @since v0.4.5
|
|
339
751
|
*/
|
|
340
|
-
let rec revAppend = (list1, list2) => {
|
|
752
|
+
export let rec revAppend = (list1, list2) => {
|
|
341
753
|
match (list1) {
|
|
342
754
|
[hd, ...tl] => revAppend(tl, [hd, ...list2]),
|
|
343
|
-
[] => list2
|
|
755
|
+
[] => list2,
|
|
344
756
|
}
|
|
345
757
|
}
|
|
346
758
|
|
|
347
759
|
/**
|
|
348
760
|
* Sorts the given list based on a given comparator function. The resulting list is sorted in increasing order.
|
|
349
|
-
*
|
|
761
|
+
*
|
|
350
762
|
* Ordering is calculated using a comparator function which takes two list 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.
|
|
351
763
|
* @param comp: The comparator function used to indicate sort order
|
|
352
764
|
* @param list: The list to be sorted
|
|
765
|
+
* @returns The sorted list
|
|
766
|
+
*
|
|
353
767
|
* @since v0.4.5
|
|
354
768
|
*/
|
|
355
|
-
let sort = (comp, list) => {
|
|
769
|
+
export let sort = (comp, list) => {
|
|
356
770
|
let rec merge = (left, right, list) => {
|
|
357
|
-
match((left, right)){
|
|
771
|
+
match ((left, right)) {
|
|
358
772
|
(_, []) => {
|
|
359
773
|
revAppend(list, left)
|
|
360
774
|
},
|
|
@@ -362,19 +776,19 @@ let sort = (comp, list) => {
|
|
|
362
776
|
revAppend(list, right)
|
|
363
777
|
},
|
|
364
778
|
([lhd, ...ltl], [rhd, ...rtl]) => {
|
|
365
|
-
if(comp(lhd, rhd) < 0){
|
|
366
|
-
merge(ltl, right, append([lhd],list))
|
|
367
|
-
}else{
|
|
779
|
+
if (comp(lhd, rhd) < 0) {
|
|
780
|
+
merge(ltl, right, append([lhd], list))
|
|
781
|
+
} else {
|
|
368
782
|
merge(left, rtl, append([rhd], list))
|
|
369
783
|
}
|
|
370
|
-
}
|
|
784
|
+
},
|
|
371
785
|
}
|
|
372
786
|
}
|
|
373
787
|
|
|
374
|
-
let rec mergesort =
|
|
375
|
-
if(length(list) <= 1){
|
|
788
|
+
let rec mergesort = list => {
|
|
789
|
+
if (length(list) <= 1) {
|
|
376
790
|
list
|
|
377
|
-
}else{
|
|
791
|
+
} else {
|
|
378
792
|
let middle = length(list) / 2
|
|
379
793
|
let (left, right) = part(middle, list)
|
|
380
794
|
merge(mergesort(left), mergesort(right), [])
|