@grain/stdlib 0.5.3 → 0.5.4
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 +38 -0
- package/array.gr +5 -0
- package/array.md +30 -0
- package/char.gr +2 -2
- package/immutablemap.gr +493 -0
- package/immutablemap.md +479 -0
- package/immutablepriorityqueue.gr +33 -5
- package/immutablepriorityqueue.md +44 -1
- package/immutableset.gr +498 -0
- package/immutableset.md +449 -0
- package/list.gr +2 -2
- package/marshal.gr +4 -4
- package/number.gr +648 -23
- package/number.md +284 -24
- package/package.json +1 -1
- package/priorityqueue.gr +25 -5
- package/priorityqueue.md +30 -0
- package/queue.gr +14 -1
- package/queue.md +16 -1
- package/regex.gr +85 -62
- package/runtime/bigint.gr +4 -4
- package/runtime/compare.gr +2 -1
- package/runtime/equal.gr +2 -1
- package/runtime/exception.gr +9 -5
- package/runtime/exception.md +8 -2
- package/runtime/gc.gr +2 -1
- package/runtime/malloc.gr +1 -3
- package/runtime/numberUtils.gr +11 -11
- package/runtime/numbers.gr +120 -36
- package/runtime/numbers.md +26 -0
- package/runtime/string.gr +4 -2
- package/set.gr +25 -25
- package/stack.gr +12 -0
- package/stack.md +15 -0
- package/string.gr +312 -38
- package/string.md +99 -0
- package/sys/file.gr +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
### [0.5.4](https://github.com/grain-lang/grain/compare/stdlib-v0.5.3...stdlib-v0.5.4) (2022-11-12)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **stdlib:** Add `empty` constant to ImmutablePriorityQueue module ([427335f](https://github.com/grain-lang/grain/commit/427335fa5c211445f727a650ca06adacfe9c5310))
|
|
9
|
+
* **stdlib:** Add `empty` constant to Queue module ([427335f](https://github.com/grain-lang/grain/commit/427335fa5c211445f727a650ca06adacfe9c5310))
|
|
10
|
+
* **stdlib:** Add `empty` constant to Stack module ([427335f](https://github.com/grain-lang/grain/commit/427335fa5c211445f727a650ca06adacfe9c5310))
|
|
11
|
+
* **stdlib:** Add `exp` function to Number module ([5af9a99](https://github.com/grain-lang/grain/commit/5af9a99b2ec3b4a2d6745cb22b70defe2b366cfa))
|
|
12
|
+
* **stdlib:** Add `factorial` function to Number module ([5af9a99](https://github.com/grain-lang/grain/commit/5af9a99b2ec3b4a2d6745cb22b70defe2b366cfa))
|
|
13
|
+
* **stdlib:** Add `gamma` function to Number module ([5af9a99](https://github.com/grain-lang/grain/commit/5af9a99b2ec3b4a2d6745cb22b70defe2b366cfa))
|
|
14
|
+
* **stdlib:** Add `infinity` constant to the Number module ([c24f6c1](https://github.com/grain-lang/grain/commit/c24f6c1cfae87632a003c0337c29ec98a80cfda2))
|
|
15
|
+
* **stdlib:** Add `nan` constant to the Number module ([c24f6c1](https://github.com/grain-lang/grain/commit/c24f6c1cfae87632a003c0337c29ec98a80cfda2))
|
|
16
|
+
* **stdlib:** Add `pow` function to Number module ([5af9a99](https://github.com/grain-lang/grain/commit/5af9a99b2ec3b4a2d6745cb22b70defe2b366cfa))
|
|
17
|
+
* **stdlib:** Add `replaceAll` function to String module ([5606cd2](https://github.com/grain-lang/grain/commit/5606cd246583884175b135cbeb29024400651b34))
|
|
18
|
+
* **stdlib:** Add `replaceFirst` function to String module ([5606cd2](https://github.com/grain-lang/grain/commit/5606cd246583884175b135cbeb29024400651b34))
|
|
19
|
+
* **stdlib:** Add `replaceLast` function to String module ([5606cd2](https://github.com/grain-lang/grain/commit/5606cd246583884175b135cbeb29024400651b34))
|
|
20
|
+
* **stdlib:** Add `tan` function to Number module ([5af9a99](https://github.com/grain-lang/grain/commit/5af9a99b2ec3b4a2d6745cb22b70defe2b366cfa))
|
|
21
|
+
* **stdlib:** Add `toDegrees` function to Number module ([5af9a99](https://github.com/grain-lang/grain/commit/5af9a99b2ec3b4a2d6745cb22b70defe2b366cfa))
|
|
22
|
+
* **stdlib:** Add `toRadians` function to Number module ([5af9a99](https://github.com/grain-lang/grain/commit/5af9a99b2ec3b4a2d6745cb22b70defe2b366cfa))
|
|
23
|
+
* **stdlib:** Add additional functions to Number module ([#1443](https://github.com/grain-lang/grain/issues/1443)) ([5af9a99](https://github.com/grain-lang/grain/commit/5af9a99b2ec3b4a2d6745cb22b70defe2b366cfa))
|
|
24
|
+
* **stdlib:** Add replacement functions to String module ([#1441](https://github.com/grain-lang/grain/issues/1441)) ([5606cd2](https://github.com/grain-lang/grain/commit/5606cd246583884175b135cbeb29024400651b34))
|
|
25
|
+
* **stdlib:** Added `empty` constant to immutable data structures ([#1466](https://github.com/grain-lang/grain/issues/1466)) ([427335f](https://github.com/grain-lang/grain/commit/427335fa5c211445f727a650ca06adacfe9c5310))
|
|
26
|
+
* **stdlib:** Implement `fromArray` in PriorityQueue & ImmutablePriorityQueue modules ([#1451](https://github.com/grain-lang/grain/issues/1451)) ([d321f84](https://github.com/grain-lang/grain/commit/d321f84174fee2a340745a9f55994fbfa23f6c7a))
|
|
27
|
+
* **stdlib:** Implement ImmutableMap and ImmutableSet ([#1414](https://github.com/grain-lang/grain/issues/1414)) ([b31120d](https://github.com/grain-lang/grain/commit/b31120d41be668c48b9bca9f2b944616371a8ab4))
|
|
28
|
+
* **stdlib:** Improved efficiency of constructing a PriorityQueue from a List ([d321f84](https://github.com/grain-lang/grain/commit/d321f84174fee2a340745a9f55994fbfa23f6c7a))
|
|
29
|
+
* **stdlib:** Optimize string trimming ([#1442](https://github.com/grain-lang/grain/issues/1442)) ([0212247](https://github.com/grain-lang/grain/commit/0212247a7fbf0d54085959de2853f3fe66cd8b12))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### Bug Fixes
|
|
33
|
+
|
|
34
|
+
* **compiler:** Panic immediately when out of memory ([#1450](https://github.com/grain-lang/grain/issues/1450)) ([943d47d](https://github.com/grain-lang/grain/commit/943d47dddde2d88fd96727e9d7ed8501efec42ef))
|
|
35
|
+
* **grainfmt:** Handle chained value bindings properly ([#1467](https://github.com/grain-lang/grain/issues/1467)) ([07bfcd3](https://github.com/grain-lang/grain/commit/07bfcd3f15c34ef99b05531591b1473f206b7395))
|
|
36
|
+
* **grainfmt:** Indent lines when wrapping infix operators ([#1465](https://github.com/grain-lang/grain/issues/1465)) ([d705849](https://github.com/grain-lang/grain/commit/d705849ea8d9073e608576b77adeae834c454e0b))
|
|
37
|
+
* **runtime:** Handle bigint mul/div within Number correctly ([#1475](https://github.com/grain-lang/grain/issues/1475)) ([0fe8aa6](https://github.com/grain-lang/grain/commit/0fe8aa6a96a9c5ebf2f2bf2e1f28578badfb337f))
|
|
38
|
+
* **stdlib:** Fix anchoring behavior in Regex.replaceAll ([#1440](https://github.com/grain-lang/grain/issues/1440)) ([d513eff](https://github.com/grain-lang/grain/commit/d513effe569d0aa0d44c974596fd285f1ad8d57d))
|
|
39
|
+
* **stdlib:** Fix handling of `NaN` and `Infinity` in Number module ([#1457](https://github.com/grain-lang/grain/issues/1457)) ([c24f6c1](https://github.com/grain-lang/grain/commit/c24f6c1cfae87632a003c0337c29ec98a80cfda2))
|
|
40
|
+
|
|
3
41
|
### [0.5.3](https://github.com/grain-lang/grain/compare/stdlib-v0.5.2...stdlib-v0.5.3) (2022-08-05)
|
|
4
42
|
|
|
5
43
|
|
package/array.gr
CHANGED
|
@@ -48,6 +48,7 @@ let initLength = length => {
|
|
|
48
48
|
* @param array: The array to inspect
|
|
49
49
|
* @returns The number of elements in the array
|
|
50
50
|
*
|
|
51
|
+
* @example Array.length([> 1, 2, 3, 4, 5]) == 5
|
|
51
52
|
* @since v0.1.0
|
|
52
53
|
*/
|
|
53
54
|
@disableGC
|
|
@@ -145,6 +146,7 @@ export let rec init /*: (Number, Number -> a) -> Array<a>*/ =
|
|
|
145
146
|
* @param index: The index to access
|
|
146
147
|
* @param array: The array to access
|
|
147
148
|
* @returns The element from the array
|
|
149
|
+
* @example Array.get(1,[> 1, 2, 3, 4, 5]) == 2
|
|
148
150
|
*
|
|
149
151
|
* @since v0.1.0
|
|
150
152
|
* @history v0.2.0: Argument order changed to data-last
|
|
@@ -162,6 +164,7 @@ export let get = (index, array) => {
|
|
|
162
164
|
* @param index: The index to update
|
|
163
165
|
* @param value: The value to store
|
|
164
166
|
* @param array: The array to update
|
|
167
|
+
* @example Array.set(1, 9, [> 1, 2, 3, 4, 5]) == [> 1, 9, 3, 4, 5]
|
|
165
168
|
*
|
|
166
169
|
* @since v0.1.0
|
|
167
170
|
* @history v0.2.0: Argument order changed to data-last
|
|
@@ -177,6 +180,7 @@ export let set = (index, value, array) => {
|
|
|
177
180
|
* @param array1: The array containing elements to appear first
|
|
178
181
|
* @param array2: The array containing elements to appear second
|
|
179
182
|
* @returns The new array containing elements from `array1` followed by elements from `array2`
|
|
183
|
+
* @example Array.append([> 1, 2], [> 3, 4, 5]) == [> 1, 2, 3, 4, 5]
|
|
180
184
|
*
|
|
181
185
|
* @since v0.1.0
|
|
182
186
|
*/
|
|
@@ -199,6 +203,7 @@ export let append = (array1, array2) => {
|
|
|
199
203
|
*
|
|
200
204
|
* @param arrays: A list containing all arrays to combine
|
|
201
205
|
* @returns The new array
|
|
206
|
+
* @example Array.concat([[> 1, 2], [> 3, 4], [> 5, 6]]) == [> 1, 2, 3, 4, 5, 6]
|
|
202
207
|
*
|
|
203
208
|
* @since v0.1.0
|
|
204
209
|
*/
|
package/array.md
CHANGED
|
@@ -50,6 +50,12 @@ Returns:
|
|
|
50
50
|
|----|-----------|
|
|
51
51
|
|`Number`|The number of elements in the array|
|
|
52
52
|
|
|
53
|
+
Examples:
|
|
54
|
+
|
|
55
|
+
```grain
|
|
56
|
+
Array.length([> 1, 2, 3, 4, 5]) == 5
|
|
57
|
+
```
|
|
58
|
+
|
|
53
59
|
### Array.**make**
|
|
54
60
|
|
|
55
61
|
<details disabled>
|
|
@@ -153,6 +159,12 @@ Returns:
|
|
|
153
159
|
|----|-----------|
|
|
154
160
|
|`a`|The element from the array|
|
|
155
161
|
|
|
162
|
+
Examples:
|
|
163
|
+
|
|
164
|
+
```grain
|
|
165
|
+
Array.get(1,[> 1, 2, 3, 4, 5]) == 2
|
|
166
|
+
```
|
|
167
|
+
|
|
156
168
|
### Array.**set**
|
|
157
169
|
|
|
158
170
|
<details>
|
|
@@ -184,6 +196,12 @@ Parameters:
|
|
|
184
196
|
|`value`|`a`|The value to store|
|
|
185
197
|
|`array`|`Array<a>`|The array to update|
|
|
186
198
|
|
|
199
|
+
Examples:
|
|
200
|
+
|
|
201
|
+
```grain
|
|
202
|
+
Array.set(1, 9, [> 1, 2, 3, 4, 5]) == [> 1, 9, 3, 4, 5]
|
|
203
|
+
```
|
|
204
|
+
|
|
187
205
|
### Array.**append**
|
|
188
206
|
|
|
189
207
|
<details disabled>
|
|
@@ -211,6 +229,12 @@ Returns:
|
|
|
211
229
|
|----|-----------|
|
|
212
230
|
|`Array<a>`|The new array containing elements from `array1` followed by elements from `array2`|
|
|
213
231
|
|
|
232
|
+
Examples:
|
|
233
|
+
|
|
234
|
+
```grain
|
|
235
|
+
Array.append([> 1, 2], [> 3, 4, 5]) == [> 1, 2, 3, 4, 5]
|
|
236
|
+
```
|
|
237
|
+
|
|
214
238
|
### Array.**concat**
|
|
215
239
|
|
|
216
240
|
<details disabled>
|
|
@@ -237,6 +261,12 @@ Returns:
|
|
|
237
261
|
|----|-----------|
|
|
238
262
|
|`Array<a>`|The new array|
|
|
239
263
|
|
|
264
|
+
Examples:
|
|
265
|
+
|
|
266
|
+
```grain
|
|
267
|
+
Array.concat([[> 1, 2], [> 3, 4], [> 5, 6]]) == [> 1, 2, 3, 4, 5, 6]
|
|
268
|
+
```
|
|
269
|
+
|
|
240
270
|
### Array.**copy**
|
|
241
271
|
|
|
242
272
|
<details disabled>
|
package/char.gr
CHANGED
package/immutablemap.gr
ADDED
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module ImmutableMap: An ImmutableMap holds key-value pairs. Any value may be used as a key or value. Operations on an ImmutableMap do not mutate the map's internal state.
|
|
3
|
+
* @example import ImmutableMap from "immutablemap"
|
|
4
|
+
*
|
|
5
|
+
* @since v0.5.4
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import List from "list"
|
|
9
|
+
import Array from "array"
|
|
10
|
+
import Option from "option"
|
|
11
|
+
|
|
12
|
+
// implementation based on the paper "Implementing Sets Efficiently in a
|
|
13
|
+
// Functional Language" by Stephen Adams
|
|
14
|
+
record Node<k, v> {
|
|
15
|
+
key: k,
|
|
16
|
+
val: v,
|
|
17
|
+
size: Number,
|
|
18
|
+
left: ImmutableMap<k, v>,
|
|
19
|
+
right: ImmutableMap<k, v>,
|
|
20
|
+
},
|
|
21
|
+
/**
|
|
22
|
+
* @section Types: Type declarations included in the ImmutableMap module.
|
|
23
|
+
*/
|
|
24
|
+
enum ImmutableMap<k, v> {
|
|
25
|
+
Empty,
|
|
26
|
+
Tree(Node<k, v>),
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @section Values: Functions and constants for working with ImmutableMaps.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
// semi-arbitrary value chosen for algorithm for determining when to balance
|
|
34
|
+
// trees; no tree can have a left subtree containing this number of times
|
|
35
|
+
// more elements than its right subtree or vice versa
|
|
36
|
+
let weight = 4
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* An empty map
|
|
40
|
+
*
|
|
41
|
+
* @since v0.5.4
|
|
42
|
+
*/
|
|
43
|
+
export let empty = Empty
|
|
44
|
+
|
|
45
|
+
// returns the key-value pair of the minimum key in a tree
|
|
46
|
+
let rec min = node => {
|
|
47
|
+
match (node) {
|
|
48
|
+
Tree({ key, val, left: Empty, _ }) => (key, val),
|
|
49
|
+
Tree({ left, _ }) => min(left),
|
|
50
|
+
Empty => fail "Impossible: min of empty element in ImmutableMap",
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Provides the count of key-value pairs stored within the map.
|
|
56
|
+
*
|
|
57
|
+
* @param map: The map to inspect
|
|
58
|
+
* @returns The count of key-value pairs in the map
|
|
59
|
+
*
|
|
60
|
+
* @since v0.5.4
|
|
61
|
+
*/
|
|
62
|
+
export let size = map => {
|
|
63
|
+
match (map) {
|
|
64
|
+
Empty => 0,
|
|
65
|
+
Tree({ size, _ }) => size,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Determines if the map contains no key-value pairs.
|
|
71
|
+
*
|
|
72
|
+
* @param map: The map to inspect
|
|
73
|
+
* @returns `true` if the given map is empty or `false` otherwise
|
|
74
|
+
*
|
|
75
|
+
* @since v0.5.4
|
|
76
|
+
*/
|
|
77
|
+
export let isEmpty = map => {
|
|
78
|
+
match (map) {
|
|
79
|
+
Empty => true,
|
|
80
|
+
Tree(_) => false,
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let unwrapTree = node => {
|
|
85
|
+
match (node) {
|
|
86
|
+
Empty => fail "Impossible: ImmutableMap unwrapTree got an empty tree node",
|
|
87
|
+
Tree(tree) => tree,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// helper function for creating a tree node with correct size from
|
|
92
|
+
// two balanced trees
|
|
93
|
+
let makeNode = (key, val, left, right) => {
|
|
94
|
+
Tree({ key, val, size: 1 + size(left) + size(right), left, right })
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// note: see Figure 1 of paper referenced above for visual illustration of
|
|
98
|
+
// the rotations below
|
|
99
|
+
|
|
100
|
+
// node rotation moving the left subtree of the right node to the left side
|
|
101
|
+
let singleL = (key, val, left, right) => {
|
|
102
|
+
let { key: rKey, val: rVal, left: rl, right: rr, _ } = unwrapTree(right)
|
|
103
|
+
makeNode(rKey, rVal, makeNode(key, val, left, rl), rr)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// node rotation moving left child of right tree to the root
|
|
107
|
+
let doubleL = (key, val, left, right) => {
|
|
108
|
+
let { key: rKey, val: rVal, left: rl, right: rr, _ } = unwrapTree(right)
|
|
109
|
+
let { key: rlKey, val: rlVal, left: rll, right: rlr, _ } = unwrapTree(rl)
|
|
110
|
+
makeNode(
|
|
111
|
+
rlKey,
|
|
112
|
+
rlVal,
|
|
113
|
+
makeNode(key, val, left, rll),
|
|
114
|
+
makeNode(rKey, rVal, rlr, rr)
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// node rotation moving the right subtree of the left node to the right side
|
|
119
|
+
let singleR = (key, val, left, right) => {
|
|
120
|
+
let { key: lKey, val: lVal, left: ll, right: lr, _ } = unwrapTree(left)
|
|
121
|
+
makeNode(lKey, lVal, ll, makeNode(key, val, lr, right))
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// node rotation moving right child of left tree to the root
|
|
125
|
+
let doubleR = (key, val, left, right) => {
|
|
126
|
+
let { key: lKey, val: lVal, left: ll, right: lr, _ } = unwrapTree(left)
|
|
127
|
+
let { key: lrKey, val: lrVal, left: lrl, right: lrr, _ } = unwrapTree(lr)
|
|
128
|
+
makeNode(
|
|
129
|
+
lrKey,
|
|
130
|
+
lrVal,
|
|
131
|
+
makeNode(lKey, lVal, ll, lrl),
|
|
132
|
+
makeNode(key, val, lrr, right)
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// creates a new node after either the left or right trees have just had an
|
|
137
|
+
// element inserted or removed from them, maintaining balance in the tree
|
|
138
|
+
let balancedNode = (key, val, left, right) => {
|
|
139
|
+
let makeNodeFn = if (size(left) + size(right) < 2) {
|
|
140
|
+
makeNode
|
|
141
|
+
} else if (size(right) > weight * size(left)) {
|
|
142
|
+
// if the right tree is too much larger than the left then move part of
|
|
143
|
+
// the right tree to the left side
|
|
144
|
+
let { left: rl, right: rr, _ } = unwrapTree(right)
|
|
145
|
+
if (size(rl) < size(rr)) singleL else doubleL
|
|
146
|
+
} else if (size(left) > weight * size(right)) {
|
|
147
|
+
// if the left tree is too much larger than the right then move part of
|
|
148
|
+
// the left tree to the right side
|
|
149
|
+
let { left: ll, right: lr, _ } = unwrapTree(left)
|
|
150
|
+
if (size(lr) < size(ll)) singleR else doubleR
|
|
151
|
+
} else {
|
|
152
|
+
// if neither tree is too much larger than the other then simply create
|
|
153
|
+
// a new node
|
|
154
|
+
makeNode
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
makeNodeFn(key, val, left, right)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Produces a new map containing a new key-value pair. If the key already exists in the map, the value is replaced.
|
|
162
|
+
*
|
|
163
|
+
* @param key: The unique key in the map
|
|
164
|
+
* @param value: The value to store
|
|
165
|
+
* @param map: The base map
|
|
166
|
+
* @returns A new map containing the new key-value pair
|
|
167
|
+
*
|
|
168
|
+
* @since v0.5.4
|
|
169
|
+
*/
|
|
170
|
+
export let rec set = (key, val, map) => {
|
|
171
|
+
match (map) {
|
|
172
|
+
Empty => Tree({ key, val, size: 1, left: Empty, right: Empty }),
|
|
173
|
+
Tree({ key: nodeKey, val: nodeVal, left, right, _ }) => {
|
|
174
|
+
match (compare(key, nodeKey)) {
|
|
175
|
+
cmp when cmp < 0 =>
|
|
176
|
+
balancedNode(nodeKey, nodeVal, set(key, val, left), right),
|
|
177
|
+
cmp when cmp > 0 =>
|
|
178
|
+
balancedNode(nodeKey, nodeVal, left, set(key, val, right)),
|
|
179
|
+
_ => makeNode(key, val, left, right),
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Retrieves the value for the given key.
|
|
187
|
+
*
|
|
188
|
+
* @param key: The key to access
|
|
189
|
+
* @param map: The map to access
|
|
190
|
+
* @returns `Some(value)` if the key exists in the map or `None` otherwise
|
|
191
|
+
*
|
|
192
|
+
* @since v0.5.4
|
|
193
|
+
*/
|
|
194
|
+
export let rec get = (key, map) => {
|
|
195
|
+
match (map) {
|
|
196
|
+
Empty => None,
|
|
197
|
+
Tree({ key: nodeKey, val: nodeVal, left, right, _ }) => {
|
|
198
|
+
match (compare(key, nodeKey)) {
|
|
199
|
+
cmp when cmp < 0 => get(key, left),
|
|
200
|
+
cmp when cmp > 0 => get(key, right),
|
|
201
|
+
_ => Some(nodeVal),
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Determines if the map contains the given key. In such a case, it will always contain a value for the given key.
|
|
209
|
+
*
|
|
210
|
+
* @param key: The key to search for
|
|
211
|
+
* @param map: The map to search
|
|
212
|
+
* @returns `true` if the map contains the given key or `false` otherwise
|
|
213
|
+
*
|
|
214
|
+
* @since v0.5.4
|
|
215
|
+
*/
|
|
216
|
+
export let rec contains = (key, map) => {
|
|
217
|
+
Option.isSome(get(key, map))
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// removes the minimum element from a tree
|
|
221
|
+
let rec removeMin = node => {
|
|
222
|
+
match (node) {
|
|
223
|
+
Tree({ left: Empty, right, _ }) => right,
|
|
224
|
+
Tree({ key, val, left, right, _ }) =>
|
|
225
|
+
balancedNode(key, val, removeMin(left), right),
|
|
226
|
+
_ => fail "Impossible: ImmutableMap removeMin on empty node",
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// helper function for removing a node by creating a new node containing the
|
|
231
|
+
// removed node's left and right subtrees
|
|
232
|
+
let removeInner = (left, right) => {
|
|
233
|
+
match ((left, right)) {
|
|
234
|
+
(Empty, node) | (node, Empty) => node,
|
|
235
|
+
(left, right) => {
|
|
236
|
+
let (minKey, minVal) = min(right)
|
|
237
|
+
balancedNode(minKey, minVal, left, removeMin(right))
|
|
238
|
+
},
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Produces a new map without the key-value pair corresponding to the given
|
|
244
|
+
* key. If the key doesn't exist in the map, the map will be returned unmodified.
|
|
245
|
+
*
|
|
246
|
+
* @param key: The key to exclude
|
|
247
|
+
* @param map: The map to exclude from
|
|
248
|
+
* @returns A new map without the given key
|
|
249
|
+
*
|
|
250
|
+
* @since v0.5.4
|
|
251
|
+
*/
|
|
252
|
+
export let rec remove = (key, map) => {
|
|
253
|
+
match (map) {
|
|
254
|
+
Empty => Empty,
|
|
255
|
+
Tree({ key: nodeKey, val: nodeVal, left, right, _ }) => {
|
|
256
|
+
match (compare(key, nodeKey)) {
|
|
257
|
+
cmp when cmp < 0 =>
|
|
258
|
+
balancedNode(nodeKey, nodeVal, remove(key, left), right),
|
|
259
|
+
cmp when cmp > 0 =>
|
|
260
|
+
balancedNode(nodeKey, nodeVal, left, remove(key, right)),
|
|
261
|
+
_ => removeInner(left, right),
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Produces a new map by calling an updater function that receives the
|
|
269
|
+
* previously stored value as an `Option` and returns the new value to be
|
|
270
|
+
* stored as an `Option`. If the key didn't exist previously, the value
|
|
271
|
+
* will be `None`. If `None` is returned from the updater function, the
|
|
272
|
+
* key-value pair is excluded.
|
|
273
|
+
*
|
|
274
|
+
* @param key: The unique key in the map
|
|
275
|
+
* @param fn: The updater function
|
|
276
|
+
* @param map: The base map
|
|
277
|
+
* @returns A new map with the value at the given key modified according to the function's output
|
|
278
|
+
*
|
|
279
|
+
* @since v0.5.4
|
|
280
|
+
*/
|
|
281
|
+
export let update = (key, fn, map) => {
|
|
282
|
+
let val = get(key, map)
|
|
283
|
+
match (fn(val)) {
|
|
284
|
+
Some(next) => set(key, next, map),
|
|
285
|
+
None => remove(key, map),
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Iterates the map, calling an iterator function with each key and value.
|
|
291
|
+
*
|
|
292
|
+
* @param fn: The iterator function to call with each key and value
|
|
293
|
+
* @param map: The map to iterate
|
|
294
|
+
*
|
|
295
|
+
* @since v0.5.4
|
|
296
|
+
*/
|
|
297
|
+
export let forEach = (fn, map) => {
|
|
298
|
+
let rec forEachInner = node => {
|
|
299
|
+
match (node) {
|
|
300
|
+
Empty => void,
|
|
301
|
+
Tree({ key, val, left, right, _ }) => {
|
|
302
|
+
forEachInner(left)
|
|
303
|
+
fn(key, val): Void
|
|
304
|
+
forEachInner(right)
|
|
305
|
+
},
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
forEachInner(map)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Combines all key-value pairs of a map using a reducer function.
|
|
313
|
+
*
|
|
314
|
+
* @param fn: The reducer function to call on each key and value, where the value returned will be the next accumulator value
|
|
315
|
+
* @param init: The initial value to use for the accumulator on the first iteration
|
|
316
|
+
* @param map: The map to iterate
|
|
317
|
+
* @returns The final accumulator returned from `fn`
|
|
318
|
+
*
|
|
319
|
+
* @since v0.5.4
|
|
320
|
+
*/
|
|
321
|
+
export let reduce = (fn, init, map) => {
|
|
322
|
+
let rec reduceInner = (acc, node) => {
|
|
323
|
+
match (node) {
|
|
324
|
+
Empty => acc,
|
|
325
|
+
Tree({ key, val, left, right, _ }) => {
|
|
326
|
+
let newAcc = fn(reduceInner(acc, left), key, val)
|
|
327
|
+
reduceInner(newAcc, right)
|
|
328
|
+
},
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
reduceInner(init, map)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// joins two trees with a value, preserving the BST property of left children
|
|
335
|
+
// being less the node and right children being greater than the node
|
|
336
|
+
let rec concat3 = (key, val, left, right) => {
|
|
337
|
+
match ((left, right)) {
|
|
338
|
+
(Empty, node) | (node, Empty) => set(key, val, node),
|
|
339
|
+
(Tree(left) as leftOpt, Tree(right) as rightOpt) => {
|
|
340
|
+
if (weight * left.size < right.size) {
|
|
341
|
+
balancedNode(
|
|
342
|
+
right.key,
|
|
343
|
+
right.val,
|
|
344
|
+
concat3(key, val, leftOpt, right.left),
|
|
345
|
+
right.right
|
|
346
|
+
)
|
|
347
|
+
} else if (weight * right.size < left.size) {
|
|
348
|
+
balancedNode(
|
|
349
|
+
left.key,
|
|
350
|
+
left.val,
|
|
351
|
+
left.left,
|
|
352
|
+
concat3(key, val, left.right, rightOpt)
|
|
353
|
+
)
|
|
354
|
+
} else {
|
|
355
|
+
makeNode(key, val, leftOpt, rightOpt)
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// concatenates two trees of arbitrary size
|
|
362
|
+
let concat = (node1, node2) => {
|
|
363
|
+
match (node2) {
|
|
364
|
+
Empty => node1,
|
|
365
|
+
_ => {
|
|
366
|
+
let (minKey, minVal) = min(node2)
|
|
367
|
+
concat3(minKey, minVal, node1, removeMin(node2))
|
|
368
|
+
},
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
let reduceRight = (fn, init, map) => {
|
|
373
|
+
let rec reduceInner = (acc, node) => {
|
|
374
|
+
match (node) {
|
|
375
|
+
Empty => acc,
|
|
376
|
+
Tree({ key, val, left, right, _ }) => {
|
|
377
|
+
let newAcc = fn(reduceInner(acc, right), key, val)
|
|
378
|
+
reduceInner(newAcc, left)
|
|
379
|
+
},
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
reduceInner(init, map)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Enumerates all keys in the given map.
|
|
387
|
+
*
|
|
388
|
+
* @param map: The map to enumerate
|
|
389
|
+
* @returns A list containing all keys from the given map
|
|
390
|
+
*
|
|
391
|
+
* @since v0.5.4
|
|
392
|
+
*/
|
|
393
|
+
export let keys = map => {
|
|
394
|
+
reduceRight((list, key, _) => [key, ...list], [], map)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Enumerates all values in the given map.
|
|
399
|
+
*
|
|
400
|
+
* @param map: The map to enumerate
|
|
401
|
+
* @returns A list containing all values from the given map
|
|
402
|
+
*
|
|
403
|
+
* @since v0.5.4
|
|
404
|
+
*/
|
|
405
|
+
export let values = map => {
|
|
406
|
+
reduceRight((list, _, value) => [value, ...list], [], map)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Produces a new map excluding the key-value pairs where a predicate function returns `false`.
|
|
411
|
+
*
|
|
412
|
+
* @param fn: The predicate function to indicate which key-value pairs to exclude from the map, where returning `false` indicates the key-value pair should be excluded
|
|
413
|
+
* @param map: The map to iterate
|
|
414
|
+
* @returns A new map excluding the key-value pairs not fulfilling the predicate
|
|
415
|
+
*
|
|
416
|
+
* @since v0.5.4
|
|
417
|
+
*/
|
|
418
|
+
export let filter = (fn, map) => {
|
|
419
|
+
let rec filterInner = node => {
|
|
420
|
+
match (node) {
|
|
421
|
+
Empty => Empty,
|
|
422
|
+
Tree({ key, val, left, right, _ }) => {
|
|
423
|
+
if (fn(key, val)) {
|
|
424
|
+
concat3(key, val, filterInner(left), filterInner(right))
|
|
425
|
+
} else {
|
|
426
|
+
concat(filterInner(left), filterInner(right))
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
filterInner(map)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Produces a new map excluding the key-value pairs where a predicate function returns `true`.
|
|
436
|
+
*
|
|
437
|
+
* @param fn: The predicate function to indicate which key-value pairs to exclude from the map, where returning `true` indicates the key-value pair should be excluded
|
|
438
|
+
* @param map: The map to iterate
|
|
439
|
+
* @returns A new map excluding the key-value pairs fulfilling the predicate
|
|
440
|
+
*
|
|
441
|
+
* @since v0.5.4
|
|
442
|
+
*/
|
|
443
|
+
export let reject = (fn, map) => {
|
|
444
|
+
filter((key, val) => !fn(key, val), map)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Creates a map from a list.
|
|
449
|
+
*
|
|
450
|
+
* @param list: The list to convert
|
|
451
|
+
* @returns A map containing all key-value pairs from the list
|
|
452
|
+
*
|
|
453
|
+
* @since v0.5.4
|
|
454
|
+
*/
|
|
455
|
+
export let fromList = list => {
|
|
456
|
+
List.reduce((map, (key, val)) => set(key, val, map), empty, list)
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Enumerates all key-value pairs in the given map.
|
|
461
|
+
*
|
|
462
|
+
* @param map: The map to enumerate
|
|
463
|
+
* @returns A list containing all key-value pairs from the given map
|
|
464
|
+
*
|
|
465
|
+
* @since v0.5.4
|
|
466
|
+
*/
|
|
467
|
+
export let toList = map => {
|
|
468
|
+
reduceRight((list, key, val) => [(key, val), ...list], [], map)
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Creates a map from an array.
|
|
473
|
+
*
|
|
474
|
+
* @param array: The array to convert
|
|
475
|
+
* @returns A map containing all key-value pairs from the array
|
|
476
|
+
*
|
|
477
|
+
* @since v0.5.4
|
|
478
|
+
*/
|
|
479
|
+
export let fromArray = array => {
|
|
480
|
+
Array.reduce((map, (key, val)) => set(key, val, map), empty, array)
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Converts a map into an array of its key-value pairs.
|
|
485
|
+
*
|
|
486
|
+
* @param map: The map to convert
|
|
487
|
+
* @returns An array containing all key-value pairs from the given map
|
|
488
|
+
*
|
|
489
|
+
* @since v0.5.4
|
|
490
|
+
*/
|
|
491
|
+
export let toArray = map => {
|
|
492
|
+
Array.fromList(toList(map))
|
|
493
|
+
}
|