functionalscript 0.0.182 → 0.0.190
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/btree/index.js +9 -38
- package/btree/test.js +2 -4
- package/function/operator/index.js +25 -2
- package/json/index.js +92 -0
- package/json/test.js +18 -0
- package/map/index.js +6 -5
- package/map/test.js +2 -1
- package/module-manager/index.js +3 -3
- package/package.json +1 -1
- package/sequence/asyncIterable/index.js +5 -5
- package/sequence/index.js +266 -50
- package/sequence/iterable/index.js +5 -5
- package/sequence/operator/index.js +92 -0
- package/sequence/test.js +51 -0
- package/test.js +2 -1
- package/sequence/list/index.js +0 -223
- package/sequence/list/test.js +0 -38
package/btree/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const { index3, index5 } = require('../cmp')
|
|
2
|
-
const
|
|
3
|
-
const { pipe } = require('../function')
|
|
2
|
+
const seq = require('../sequence')
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* @template T
|
|
@@ -306,66 +305,38 @@ const replaceVisitor = {
|
|
|
306
305
|
notFound: notFoundGet,
|
|
307
306
|
}
|
|
308
307
|
|
|
309
|
-
/** @type {<T>(
|
|
310
|
-
const
|
|
311
|
-
*[Symbol.iterator]() {
|
|
312
|
-
switch (node.length) {
|
|
313
|
-
case 1: case 2: {
|
|
314
|
-
yield* node
|
|
315
|
-
return
|
|
316
|
-
}
|
|
317
|
-
case 3: {
|
|
318
|
-
yield* values(node[0])
|
|
319
|
-
yield node[1]
|
|
320
|
-
yield* values(node[2])
|
|
321
|
-
return
|
|
322
|
-
}
|
|
323
|
-
default: {
|
|
324
|
-
yield* values(node[0])
|
|
325
|
-
yield node[1]
|
|
326
|
-
yield* values(node[2])
|
|
327
|
-
yield node[3]
|
|
328
|
-
yield* values(node[4])
|
|
329
|
-
return
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
})
|
|
308
|
+
/** @type {<T>(...array: readonly seq.Sequence<T>[]) => seq.Sequence<T>} */
|
|
309
|
+
const flatArray = (...array) => seq.flat(seq.fromArray(array))
|
|
334
310
|
|
|
335
|
-
/** @type {<T>(
|
|
336
|
-
const flatArray = (...array) => list.flat(list.fromArray(array))
|
|
337
|
-
|
|
338
|
-
/** @type {<T>(node: Node<T>) => list.List<T>} */
|
|
311
|
+
/** @type {<T>(node: Node<T>) => seq.Sequence<T>} */
|
|
339
312
|
const valuesList = node => () => {
|
|
340
313
|
const f = () => {
|
|
341
314
|
switch (node.length) {
|
|
342
|
-
case 1: case 2: { return
|
|
315
|
+
case 1: case 2: { return seq.fromArray(node) }
|
|
343
316
|
case 3: {
|
|
344
317
|
return flatArray(
|
|
345
318
|
valuesList(node[0]),
|
|
346
|
-
|
|
319
|
+
seq.one(node[1]),
|
|
347
320
|
valuesList(node[2])
|
|
348
321
|
)
|
|
349
322
|
}
|
|
350
323
|
default: {
|
|
351
324
|
return flatArray(
|
|
352
325
|
valuesList(node[0]),
|
|
353
|
-
|
|
326
|
+
seq.one(node[1]),
|
|
354
327
|
valuesList(node[2]),
|
|
355
|
-
|
|
328
|
+
seq.one(node[3]),
|
|
356
329
|
valuesList(node[4])
|
|
357
330
|
)
|
|
358
331
|
}
|
|
359
332
|
}
|
|
360
333
|
}
|
|
361
|
-
return
|
|
334
|
+
return seq.next(f())
|
|
362
335
|
}
|
|
363
336
|
|
|
364
337
|
module.exports = {
|
|
365
338
|
/** @readonly */
|
|
366
339
|
valuesList,
|
|
367
|
-
/** @readonly */
|
|
368
|
-
values,
|
|
369
340
|
/**
|
|
370
341
|
* @readonly
|
|
371
342
|
* @type { <T>(cmp: Cmp<T>) => (node: Node<T>) => T|undefined }
|
package/btree/test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const btree = require('.')
|
|
2
2
|
const { setVisitor, valuesList } = btree
|
|
3
3
|
const { cmp } = require('../cmp')
|
|
4
|
-
const list = require('../sequence
|
|
4
|
+
const list = require('../sequence')
|
|
5
5
|
|
|
6
6
|
/** @type {(node: btree.Node<string>) => (value: string) => btree.Node<string>} */
|
|
7
7
|
const set = node => value => {
|
|
@@ -21,11 +21,9 @@ const test = () => {
|
|
|
21
21
|
node = set(node)('f')
|
|
22
22
|
//
|
|
23
23
|
{
|
|
24
|
-
/** @type {import('../sequence
|
|
24
|
+
/** @type {import('../sequence').Result<string>} */
|
|
25
25
|
let result = list.next(valuesList(node))
|
|
26
26
|
while (result !== undefined) {
|
|
27
|
-
const t = result[0]
|
|
28
|
-
console.log(t)
|
|
29
27
|
result = list.next(result[1])
|
|
30
28
|
}
|
|
31
29
|
}
|
|
@@ -1,18 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @template A
|
|
3
|
+
* @template B
|
|
4
|
+
* @template R
|
|
5
|
+
* @typedef {(a: A) => (b: B) => R} BinaryOperator
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
/**
|
|
2
9
|
* @template R
|
|
3
10
|
* @template T
|
|
4
|
-
* @typedef {
|
|
11
|
+
* @typedef {BinaryOperator<R, T, R>} ReduceOperator
|
|
5
12
|
*/
|
|
6
13
|
|
|
7
|
-
/** @type {(separator: string) =>
|
|
14
|
+
/** @type {(separator: string) => ReduceOperator<string, string>} */
|
|
8
15
|
const join = separator => prior => value => `${prior}${separator}${value}`
|
|
9
16
|
|
|
10
17
|
/** @type {(sum: number) => (value: number) => number} */
|
|
11
18
|
const addition = a => b => a + b
|
|
12
19
|
|
|
20
|
+
/**
|
|
21
|
+
* @template T
|
|
22
|
+
* @template R
|
|
23
|
+
* @typedef {(value: T) => R} UnaryOperator
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** @type {(value: boolean) => boolean} */
|
|
27
|
+
const logicalNot = v => !v
|
|
28
|
+
|
|
29
|
+
/** @type {<T>(a: T) => (b: T) => boolean} */
|
|
30
|
+
const strictEqual = a => b => a === b
|
|
31
|
+
|
|
13
32
|
module.exports = {
|
|
14
33
|
/** @readonly */
|
|
15
34
|
join,
|
|
16
35
|
/** @readonly */
|
|
17
36
|
addition,
|
|
37
|
+
/** @readonly */
|
|
38
|
+
strictEqual,
|
|
39
|
+
/** @readonly */
|
|
40
|
+
logicalNot,
|
|
18
41
|
}
|
package/json/index.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const seq = require('../sequence')
|
|
2
|
+
const map = require('../map')
|
|
3
|
+
const op = require('../sequence/operator')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {{
|
|
7
|
+
* readonly [k in string]: Json
|
|
8
|
+
* }} Object
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** @typedef {readonly Json[]} Array */
|
|
12
|
+
|
|
13
|
+
/** @typedef {Object|boolean|string|number|null|Array} Json */
|
|
14
|
+
|
|
15
|
+
/** @type {(value: Json) => (path: readonly string[]) => (src: Json|undefined) => Json} */
|
|
16
|
+
const addProperty = value => {
|
|
17
|
+
/** @type {(path: seq.Sequence<string>) => (src: Json|undefined) => Json} */
|
|
18
|
+
const f = path => src => {
|
|
19
|
+
const result = seq.next(path)
|
|
20
|
+
if (result === undefined) { return value }
|
|
21
|
+
const srcObject = (src === undefined || src === null || typeof src !== 'object' || src instanceof Array) ? {} : src
|
|
22
|
+
const [name, tail] = result
|
|
23
|
+
return { ...srcObject, [name]: f(tail)(srcObject[name]) }
|
|
24
|
+
}
|
|
25
|
+
return path => f(seq.fromArray(path))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** @type {(kv: readonly[string, seq.Sequence<string>]) => seq.Sequence<string>} */
|
|
29
|
+
const property = ([k, v]) => seq.concat(seq.one(JSON.stringify(k)), seq.one(':'), v)
|
|
30
|
+
|
|
31
|
+
/** @type {op.Scan<seq.Sequence<string>, seq.Sequence<string>>} */
|
|
32
|
+
const commaValue = a => [seq.concat(seq.one(','), a), commaValue]
|
|
33
|
+
|
|
34
|
+
/** @type {op.Scan<seq.Sequence<string>, seq.Sequence<string>>} */
|
|
35
|
+
const joinScan = value => [value, commaValue]
|
|
36
|
+
|
|
37
|
+
/** @type {seq.SequenceMap<seq.Sequence<string>, string>} */
|
|
38
|
+
const join = input => seq.flat(seq.scan(joinScan)(input))
|
|
39
|
+
|
|
40
|
+
/** @type {(open: string) => (close: string) => (input: seq.Sequence<seq.Sequence<string>>) => seq.Sequence<string>} */
|
|
41
|
+
const list = open => close => {
|
|
42
|
+
const seqOpen = seq.one(open)
|
|
43
|
+
const seqClose = seq.one(close)
|
|
44
|
+
return input => seq.concat(seqOpen, join(input), seqClose)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const objectList = list('{')('}')
|
|
48
|
+
|
|
49
|
+
const arrayList = list('[')(']')
|
|
50
|
+
|
|
51
|
+
/** @type {(object: Object) => seq.Sequence<string>} */
|
|
52
|
+
const objectStringify = object => {
|
|
53
|
+
/** @type {map.Map<seq.Sequence<string>>} */
|
|
54
|
+
let m = map.empty
|
|
55
|
+
for (const [k, v] of Object.entries(object)) {
|
|
56
|
+
m = m.set(k)(stringSeq(v))
|
|
57
|
+
}
|
|
58
|
+
return objectList(seq.map(property)(m.entries))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** @type {(array: Array) => seq.Sequence<string>} */
|
|
62
|
+
const arrayStringify = array => arrayList(seq.map(stringSeq)(seq.fromArray(array)))
|
|
63
|
+
|
|
64
|
+
/** @type {(value: Json) => seq.Sequence<string>} */
|
|
65
|
+
const stringSeq = value => {
|
|
66
|
+
const x = typeof value
|
|
67
|
+
switch (typeof value) {
|
|
68
|
+
case 'boolean': { return seq.one(value ? "true" : "false") }
|
|
69
|
+
// Note: we shouldn't use JSON.stringify since it has non determenistic behavior.
|
|
70
|
+
// In particular: property order could be different.
|
|
71
|
+
case 'number': case 'string': { return seq.one(JSON.stringify(value)) }
|
|
72
|
+
default: {
|
|
73
|
+
if (value === null) { return seq.one("null") }
|
|
74
|
+
if (value instanceof Array) { return arrayStringify(value) }
|
|
75
|
+
return objectStringify(value)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* A deterministic version of `JSON.stringify`
|
|
82
|
+
*
|
|
83
|
+
* @type {(value: Json) => string}
|
|
84
|
+
*/
|
|
85
|
+
const stringify = value => seq.join('')(stringSeq(value))
|
|
86
|
+
|
|
87
|
+
module.exports = {
|
|
88
|
+
/** @readonly */
|
|
89
|
+
addProperty,
|
|
90
|
+
/** @readonly */
|
|
91
|
+
stringify,
|
|
92
|
+
}
|
package/json/test.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const json = require('.')
|
|
2
|
+
|
|
3
|
+
if (json.addProperty("Hello")([])({}) !== "Hello") { throw 'error' }
|
|
4
|
+
|
|
5
|
+
{
|
|
6
|
+
const x = json.stringify(json.addProperty("Hello")(['a'])({}))
|
|
7
|
+
if (x !== '{"a":"Hello"}') { throw x }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
const x = json.stringify(json.addProperty("Hello")(['a'])({c:[],b:12}))
|
|
12
|
+
if (x !== '{"a":"Hello","b":12,"c":[]}') { throw x }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
{
|
|
16
|
+
const x = json.stringify(json.addProperty("Hello")(['a', 'x'])({ a: { y: [24] }, c: [], b: 12 }))
|
|
17
|
+
if (x !== '{"a":{"x":"Hello","y":[24]},"b":12,"c":[]}') { throw x }
|
|
18
|
+
}
|
package/map/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const option = require("../option")
|
|
2
|
-
const { getVisitor, setVisitor,
|
|
2
|
+
const { getVisitor, setVisitor, valuesList } = require("../btree")
|
|
3
3
|
const { cmp } = require("../cmp")
|
|
4
|
+
const list = require("../sequence")
|
|
4
5
|
|
|
5
6
|
/** @typedef {import("../cmp").Sign} Sign */
|
|
6
7
|
|
|
@@ -29,7 +30,7 @@ const { cmp } = require("../cmp")
|
|
|
29
30
|
* @typedef {{
|
|
30
31
|
* readonly get: (name: string) => T|undefined
|
|
31
32
|
* readonly set: (name: string) => (value: T) => Map<T>
|
|
32
|
-
* readonly entries:
|
|
33
|
+
* readonly entries: list.Sequence<Entry<T>>
|
|
33
34
|
* readonly root: undefined|TNode<Entry<T>>
|
|
34
35
|
* }} Map
|
|
35
36
|
*/
|
|
@@ -46,7 +47,7 @@ const create = root => ({
|
|
|
46
47
|
if ('overflow' in result) { return create(result.overflow) }
|
|
47
48
|
throw ''
|
|
48
49
|
},
|
|
49
|
-
entries: (
|
|
50
|
+
entries: valuesList(root),
|
|
50
51
|
root,
|
|
51
52
|
})
|
|
52
53
|
|
|
@@ -54,14 +55,14 @@ const create = root => ({
|
|
|
54
55
|
* @type {{
|
|
55
56
|
* readonly get: (name: string) => undefined
|
|
56
57
|
* readonly set: (name: string) => <T>(value: T) => Map<T>
|
|
57
|
-
* readonly entries: () =>
|
|
58
|
+
* readonly entries: () => undefined
|
|
58
59
|
* readonly root: undefined
|
|
59
60
|
* }}
|
|
60
61
|
*/
|
|
61
62
|
const empty = {
|
|
62
63
|
get: () => undefined,
|
|
63
64
|
set: name => value => create([[name, value]]),
|
|
64
|
-
entries:
|
|
65
|
+
entries: list.empty,
|
|
65
66
|
root: undefined
|
|
66
67
|
}
|
|
67
68
|
|
package/map/test.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { empty } = require('.')
|
|
2
|
+
const list = require('../sequence')
|
|
2
3
|
|
|
3
4
|
{
|
|
4
5
|
let m = empty.set('a')(1)
|
|
@@ -44,7 +45,7 @@ const { empty } = require('.')
|
|
|
44
45
|
if (m.get('x') !== 12) { throw 'error' }
|
|
45
46
|
if (m.get('y') !== 44) { throw 'error' }
|
|
46
47
|
if (m.get('a') !== undefined) { throw 'error' }
|
|
47
|
-
const entries = Array.from(m.entries
|
|
48
|
+
const entries = Array.from(list.iterable(m.entries))
|
|
48
49
|
if (entries.length !== 2) { throw 'error' }
|
|
49
50
|
}
|
|
50
51
|
|
package/module-manager/index.js
CHANGED
|
@@ -3,7 +3,7 @@ const { pipe } = require('../function')
|
|
|
3
3
|
const option = require('../option')
|
|
4
4
|
const { head, last, splitLast, splitFirst } = array
|
|
5
5
|
const iter = require('../sequence/iterable')
|
|
6
|
-
const seq = require('../sequence')
|
|
6
|
+
const seq = require('../sequence/operator')
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @template T
|
|
@@ -39,8 +39,8 @@ const seq = require('../sequence')
|
|
|
39
39
|
|
|
40
40
|
/** @typedef {(_: string) => undefined|Package|Dependencies} Dependencies */
|
|
41
41
|
|
|
42
|
-
/** @type {seq.
|
|
43
|
-
const pathNormReduce = seq.
|
|
42
|
+
/** @type {seq.ExclusiveScan<string, undefined|Path>} */
|
|
43
|
+
const pathNormReduce = seq.exclusiveScan
|
|
44
44
|
(path => item =>
|
|
45
45
|
path === undefined ?
|
|
46
46
|
undefined :
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { pipe } = require('../../function')
|
|
2
|
-
const seq = require('
|
|
2
|
+
const seq = require('../operator')
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @template T
|
|
@@ -55,10 +55,10 @@ const scan = s => c => ({
|
|
|
55
55
|
}
|
|
56
56
|
})
|
|
57
57
|
|
|
58
|
-
/** @type {<T, R>(s: seq.
|
|
59
|
-
const
|
|
58
|
+
/** @type {<T, R>(s: seq.ExclusiveScan<T, R>) => (c: AsyncOrSyncIterable<T>) => AsyncIterable<R>} */
|
|
59
|
+
const exclusiveScan = ([first, s]) => c => concat([first])(scan(s)(c))
|
|
60
60
|
|
|
61
|
-
/** @type {<T, R>(is: seq.
|
|
61
|
+
/** @type {<T, R>(is: seq.ExclusiveScan<T, R>) => (c: AsyncOrSyncIterable<T>) => Promise<R>} */
|
|
62
62
|
const reduce = ([first, s]) => async c => {
|
|
63
63
|
let next = first
|
|
64
64
|
for await (const i of scan(s)(c)) {
|
|
@@ -105,7 +105,7 @@ module.exports = {
|
|
|
105
105
|
/** @readonly */
|
|
106
106
|
scan,
|
|
107
107
|
/** @readonly */
|
|
108
|
-
|
|
108
|
+
exclusiveScan,
|
|
109
109
|
/** @readonly */
|
|
110
110
|
takeWhile,
|
|
111
111
|
}
|
package/sequence/index.js
CHANGED
|
@@ -1,93 +1,309 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
1
|
+
const array = require('./array')
|
|
2
|
+
const seqOp = require('./operator')
|
|
3
|
+
const { pipe } = require('../function')
|
|
4
|
+
const { logicalNot, strictEqual } = require('../function/operator')
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
|
-
* @template
|
|
6
|
-
* @
|
|
7
|
-
* @typedef {import('./array').Tuple2<T0, T1>} Tuple2
|
|
7
|
+
* @template T
|
|
8
|
+
* @typedef {() => Result<T>} SequenceFn
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @template T
|
|
12
|
-
* @
|
|
13
|
-
* @typedef {Tuple2<R, Scan<T, R>>} ScanResult
|
|
13
|
+
* @typedef {readonly [Sequence<T>, Sequence<T>]} Concat
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
+
* Use `next` function to get `first` and `tail` of the list.
|
|
18
|
+
*
|
|
19
|
+
* Please note that the sequence also contains `Concat<T>. We need this as
|
|
20
|
+
* a workaround because modern JavaScript implementations don't support
|
|
21
|
+
* ES6 TCO (Tail Call Optimization). Without this wotkaround we may have
|
|
22
|
+
* a stack overflow if a list contains a lot of concateneted lists.
|
|
23
|
+
*
|
|
17
24
|
* @template T
|
|
18
|
-
* @
|
|
19
|
-
* @typedef {(value: T) => ScanResult<T, R>} Scan
|
|
25
|
+
* @typedef { SequenceFn<T> | Concat<T>} Sequence
|
|
20
26
|
*/
|
|
21
27
|
|
|
22
28
|
/**
|
|
23
29
|
* @template T
|
|
24
|
-
* @
|
|
25
|
-
* @typedef {Tuple2<R, Scan<T, R>>} InclusiveScan
|
|
30
|
+
* @typedef {FirstAndTail<T>|undefined} Result<T>
|
|
26
31
|
*/
|
|
27
32
|
|
|
28
|
-
/**
|
|
29
|
-
const scan = operator => {
|
|
30
|
-
/** @typedef {typeof operator extends op.BinaryOperator<infer R, infer T> ? [R, T] : never} RT */
|
|
31
|
-
/** @typedef {RT[0]} R */
|
|
32
|
-
/** @typedef {RT[1]} T */
|
|
33
|
-
/** @type {(prior: R) => Scan<T, R>} */
|
|
34
|
-
const f = prior => value => {
|
|
35
|
-
const result = operator(prior)(value)
|
|
36
|
-
return [result, f(result)]
|
|
37
|
-
}
|
|
38
|
-
return f
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** @type {<R, T>(operator: op.BinaryOperator<R, T>) => (first: R) => InclusiveScan<T, R>} */
|
|
42
|
-
const inclusiveScan = operator => first => [first, scan(operator)(first)]
|
|
43
|
-
|
|
44
|
-
/**
|
|
33
|
+
/**
|
|
45
34
|
* @template T
|
|
46
|
-
* @typedef {Tuple2<
|
|
35
|
+
* @typedef {array.Tuple2<T, Sequence<T>>} FirstAndTail
|
|
47
36
|
*/
|
|
48
37
|
|
|
49
|
-
|
|
50
|
-
const createEntries = index => value => [[index, value], createEntries(index + 1)]
|
|
51
|
-
|
|
52
|
-
const entries = createEntries(0)
|
|
38
|
+
const empty = () => undefined
|
|
53
39
|
|
|
54
|
-
/** @type {(
|
|
55
|
-
const
|
|
40
|
+
/** @type {<F, T>(a: readonly[F, Sequence<T>]) => (b: Sequence<T>) => readonly[F, Sequence<T>]} */
|
|
41
|
+
const norm = ([a0, a1]) => b => [a0, [a1, b]]
|
|
56
42
|
|
|
57
|
-
|
|
43
|
+
/** @type {<T>(input: Sequence<T>) => Result<T>} */
|
|
44
|
+
const next = input => {
|
|
45
|
+
let i = input
|
|
46
|
+
while (true) {
|
|
47
|
+
if (typeof i === 'function') { return i() }
|
|
48
|
+
const [a, b] = i
|
|
49
|
+
if (typeof a === 'function') {
|
|
50
|
+
const result = a()
|
|
51
|
+
if (result !== undefined) {
|
|
52
|
+
return norm(result)(b)
|
|
53
|
+
}
|
|
54
|
+
i = b
|
|
55
|
+
} else {
|
|
56
|
+
i = norm(a)(b)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
58
60
|
|
|
59
|
-
/** @type {(
|
|
60
|
-
const
|
|
61
|
+
/** @type {<T>(input: Sequence<T>) => T|undefined} */
|
|
62
|
+
const first = input => {
|
|
63
|
+
const result = next(input)
|
|
64
|
+
if (result === undefined) { return undefined }
|
|
65
|
+
return result[0]
|
|
66
|
+
}
|
|
61
67
|
|
|
62
|
-
|
|
68
|
+
/**
|
|
69
|
+
* @template T
|
|
70
|
+
* @template R
|
|
71
|
+
* @typedef {(list: Sequence<T>) => Sequence<R>} SequenceMap
|
|
72
|
+
*/
|
|
63
73
|
|
|
64
74
|
/**
|
|
65
75
|
* @template T
|
|
66
76
|
* @template R
|
|
67
|
-
* @typedef {(
|
|
77
|
+
* @typedef {(list: Sequence<T>) => R} SequenceReduce
|
|
68
78
|
*/
|
|
69
79
|
|
|
70
|
-
/** @type {<T
|
|
71
|
-
const
|
|
80
|
+
/** @type {<T>(first: T) => Sequence<T>} */
|
|
81
|
+
const one = first => () => [first, empty]
|
|
82
|
+
|
|
83
|
+
/** @type {<T>(array: array.Array<T>) => Sequence<T>} */
|
|
84
|
+
const fromArray = a => {
|
|
85
|
+
/** @typedef {typeof a extends array.Array<infer T> ? T : never} T */
|
|
86
|
+
/** @type {(index: number) => Sequence<T>} */
|
|
87
|
+
const at = index => () => {
|
|
88
|
+
const result = array.at(index)(a)
|
|
89
|
+
if (result === undefined) { return undefined }
|
|
90
|
+
return [result[0], at(index + 1)]
|
|
91
|
+
}
|
|
92
|
+
return at(0)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** @type {<T>(a: Sequence<T>) => SequenceMap<T, T>} */
|
|
96
|
+
const concat2 = a => b => [a, b]
|
|
97
|
+
|
|
98
|
+
/** @type {<T>(...array: readonly Sequence<T>[]) => Sequence<T>} */
|
|
99
|
+
const concat = (...a) => flat(fromArray(a))
|
|
100
|
+
|
|
101
|
+
/** @type {<T, R>(f: (value: T) => Sequence<R>) => SequenceMap<T, R>} */
|
|
102
|
+
const flatMap = f => input => () => {
|
|
103
|
+
let i = input
|
|
104
|
+
while (true) {
|
|
105
|
+
const result = next(i)
|
|
106
|
+
if (result === undefined) { return undefined }
|
|
107
|
+
const [first, tail] = result
|
|
108
|
+
const firstResult = next(f(first))
|
|
109
|
+
if (firstResult !== undefined) {
|
|
110
|
+
return norm(firstResult)(flatMap(f)(tail))
|
|
111
|
+
}
|
|
112
|
+
i = tail
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** @type {<T>(list: Sequence<Sequence<T>>) => Sequence<T>} */
|
|
117
|
+
const flat = flatMap(i => i)
|
|
118
|
+
|
|
119
|
+
/** @type {<T, R>(f: (value: T) => R) => SequenceMap<T, R>} */
|
|
120
|
+
const map = f => flatMap(i => one(f(i)))
|
|
121
|
+
|
|
122
|
+
/** @type {<T>(f: (value: T) => boolean) => SequenceMap<T, T>} */
|
|
123
|
+
const filter = f => flatMap(i => f(i) ? one(i) : empty)
|
|
124
|
+
|
|
125
|
+
/** @type {<T, R>(f: (value: T) => R|undefined) => (value: T) => Sequence<R>} */
|
|
126
|
+
const filterMapFunc = f => i => {
|
|
127
|
+
const result = f(i)
|
|
128
|
+
if (result === undefined) { return empty }
|
|
129
|
+
return one(result)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** @type {<T, R>(f: (value: T) => R|undefined) => SequenceMap<T, R>} */
|
|
133
|
+
const filterMap = f => flatMap(filterMapFunc(f))
|
|
134
|
+
|
|
135
|
+
/** @type {<T, R>(s: seqOp.Scan<T, R>) => SequenceMap<T, R>} */
|
|
136
|
+
const scan = s => input => () => {
|
|
137
|
+
const result = next(input)
|
|
138
|
+
if (result === undefined) {
|
|
139
|
+
return result
|
|
140
|
+
}
|
|
141
|
+
const [first, tail] = result
|
|
142
|
+
const [newFirst, newS] = s(first)
|
|
143
|
+
return [newFirst, scan(newS)(tail)]
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** @type {<T, R>(s: seqOp.ExclusiveScan<T, R>) => SequenceMap<T, R>} */
|
|
147
|
+
const exclusiveScan = ([first, s]) => input => () => [first, scan(s)(input)]
|
|
148
|
+
|
|
149
|
+
/** @type {<T>(def: T) => (input: Sequence<T>) => T} */
|
|
150
|
+
const last = def => input => {
|
|
151
|
+
let r = def
|
|
152
|
+
let i = input
|
|
153
|
+
while (true) {
|
|
154
|
+
const result = next(i)
|
|
155
|
+
if (result === undefined) {
|
|
156
|
+
return r
|
|
157
|
+
}
|
|
158
|
+
r = result[0]
|
|
159
|
+
i = result[1]
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** @type {<T, R>(s: seqOp.ExclusiveScan<T, R>) => (input: Sequence<T>) => R} */
|
|
164
|
+
const reduce = ([first, s]) => input => last(first)(scan(s)(input))
|
|
165
|
+
|
|
166
|
+
const entries = scan(seqOp.entries)
|
|
167
|
+
|
|
168
|
+
const sum = reduce(seqOp.sum)
|
|
169
|
+
|
|
170
|
+
const length = reduce(seqOp.length)
|
|
171
|
+
|
|
172
|
+
const join = pipe(seqOp.join)(reduce)
|
|
173
|
+
|
|
174
|
+
/** @type {<T>(f: (value: T) => boolean) => SequenceMap<T, T>} */
|
|
175
|
+
const takeWhile = f => input => () => {
|
|
176
|
+
const result = next(input)
|
|
177
|
+
if (result === undefined || !f(result[0])) { return undefined }
|
|
178
|
+
return result
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** @type {(n: number) => <T>(input: Sequence<T>) => Sequence<T>} */
|
|
182
|
+
const drop = n => input => () => {
|
|
183
|
+
let iN = n
|
|
184
|
+
let iInput = input
|
|
185
|
+
while (true) {
|
|
186
|
+
const result = next(iInput)
|
|
187
|
+
if (iN <= 0 || result === undefined) { return result }
|
|
188
|
+
iN = iN - 1
|
|
189
|
+
iInput = result[1]
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** @type {(n: number) => <T>(input: Sequence<T>) => T|undefined} */
|
|
194
|
+
const at = n => input => first(drop(n)(input))
|
|
195
|
+
|
|
196
|
+
/** @type {<T>(f: (value: T) => boolean) => SequenceReduce<T, T|undefined>} */
|
|
197
|
+
const find = f => input => first(filter(f)(input))
|
|
198
|
+
|
|
199
|
+
/** @type {<T>(f: (value: T) => boolean) => SequenceReduce<T, boolean>} */
|
|
200
|
+
const some = f => input => find(x => x)(map(f)(input)) !== undefined
|
|
201
|
+
|
|
202
|
+
/** @type {<T>(value: T) => SequenceReduce<T, boolean>} */
|
|
203
|
+
const includes = value => some(strictEqual(value))
|
|
72
204
|
|
|
73
|
-
/** @type {<T
|
|
74
|
-
const
|
|
205
|
+
/** @type {<T>(f: (value: T) => boolean) => SequenceReduce<T, boolean>} */
|
|
206
|
+
const every = f => input => !some(pipe(f)(logicalNot))(input)
|
|
207
|
+
|
|
208
|
+
/** @type {<T>(list: Sequence<T>) => Iterable<T>} */
|
|
209
|
+
const iterable = list => ({
|
|
210
|
+
*[Symbol.iterator]() {
|
|
211
|
+
let i = list
|
|
212
|
+
while (true) {
|
|
213
|
+
const result = next(i)
|
|
214
|
+
if (result === undefined) { return }
|
|
215
|
+
yield result[0]
|
|
216
|
+
i = result[1]
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
/** @type {<T>(list: Sequence<T>) => AsyncIterable<T>} */
|
|
222
|
+
const asyncIterable = list => ({
|
|
223
|
+
async *[Symbol.asyncIterator]() {
|
|
224
|
+
let i = list
|
|
225
|
+
while (true) {
|
|
226
|
+
const result = next(i)
|
|
227
|
+
if (result === undefined) { return }
|
|
228
|
+
yield result[0]
|
|
229
|
+
i = result[1]
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
/** @type {<A>(a: Sequence<A>) => <B>(b: Sequence<B>) => Sequence<array.Tuple2<A, B>>} */
|
|
235
|
+
const zip = a => b => () => {
|
|
236
|
+
const resultA = next(a)
|
|
237
|
+
if (resultA === undefined) { return undefined }
|
|
238
|
+
const resultB = next(b)
|
|
239
|
+
if (resultB === undefined) { return undefined }
|
|
240
|
+
return [[resultA[0], resultB[0]], zip(resultA[1])(resultB[1])]
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** @type {<T>(list: Sequence<T>) => readonly T[]} */
|
|
244
|
+
const toArray = input => Array.from(iterable(input))
|
|
75
245
|
|
|
76
246
|
module.exports = {
|
|
77
247
|
/** @readonly */
|
|
78
|
-
|
|
248
|
+
next,
|
|
249
|
+
/** @readonly */
|
|
250
|
+
one,
|
|
251
|
+
/** @readonly */
|
|
252
|
+
empty,
|
|
253
|
+
/** @readonly */
|
|
254
|
+
at,
|
|
255
|
+
/** @readonly */
|
|
256
|
+
concat,
|
|
257
|
+
/** @readonly */
|
|
258
|
+
concat2,
|
|
259
|
+
/** @readonly */
|
|
260
|
+
first,
|
|
261
|
+
/** @readonly */
|
|
262
|
+
fromArray,
|
|
263
|
+
/** @readonly */
|
|
264
|
+
toArray,
|
|
265
|
+
/** @readonly */
|
|
266
|
+
iterable,
|
|
267
|
+
/** @readonly */
|
|
268
|
+
asyncIterable,
|
|
269
|
+
/** @readonly */
|
|
270
|
+
flatMap,
|
|
271
|
+
/** @readonly */
|
|
272
|
+
flat,
|
|
273
|
+
/** @readonly */
|
|
274
|
+
map,
|
|
275
|
+
/** @readonly */
|
|
276
|
+
filter,
|
|
277
|
+
/** @readonly */
|
|
278
|
+
filterMap,
|
|
79
279
|
/** @readonly */
|
|
80
280
|
scan,
|
|
81
281
|
/** @readonly */
|
|
82
|
-
|
|
282
|
+
exclusiveScan,
|
|
283
|
+
/** @readonly */
|
|
284
|
+
last,
|
|
285
|
+
/** @readonly */
|
|
286
|
+
reduce,
|
|
287
|
+
/** @readonly */
|
|
288
|
+
entries,
|
|
83
289
|
/** @readonly */
|
|
84
290
|
sum,
|
|
85
291
|
/** @readonly */
|
|
292
|
+
join,
|
|
293
|
+
/** @readonly */
|
|
86
294
|
length,
|
|
87
295
|
/** @readonly */
|
|
88
|
-
|
|
296
|
+
drop,
|
|
89
297
|
/** @readonly */
|
|
90
|
-
|
|
298
|
+
find,
|
|
91
299
|
/** @readonly */
|
|
92
|
-
|
|
93
|
-
|
|
300
|
+
takeWhile,
|
|
301
|
+
/** @readonly */
|
|
302
|
+
some,
|
|
303
|
+
/** @readonly */
|
|
304
|
+
every,
|
|
305
|
+
/** @readonly */
|
|
306
|
+
includes,
|
|
307
|
+
/** @readonly */
|
|
308
|
+
zip,
|
|
309
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { pipe } = require('../../function')
|
|
2
|
-
const seq = require('
|
|
2
|
+
const seq = require('../operator')
|
|
3
3
|
|
|
4
4
|
/** @type {<T>(a: Iterable<T>) => (b: Iterable<T>) => Iterable<T>} */
|
|
5
5
|
const concat = a => b => ({
|
|
@@ -21,10 +21,10 @@ const scan = s => c => ({
|
|
|
21
21
|
}
|
|
22
22
|
})
|
|
23
23
|
|
|
24
|
-
/** @type {<T, R>(s: seq.
|
|
25
|
-
const
|
|
24
|
+
/** @type {<T, R>(s: seq.ExclusiveScan<T, R>) => (c: Iterable<T>) => Iterable<R>} */
|
|
25
|
+
const exclusiveScan = ([first, s]) => c => concat([first])(scan(s)(c))
|
|
26
26
|
|
|
27
|
-
/** @type {<T, R>(s: seq.
|
|
27
|
+
/** @type {<T, R>(s: seq.ExclusiveScan<T, R>) => (c: Iterable<T>) => R} */
|
|
28
28
|
const reduce = ([first, s]) => c => {
|
|
29
29
|
let next = first
|
|
30
30
|
for (const i of scan(s)(c)) {
|
|
@@ -93,7 +93,7 @@ module.exports = {
|
|
|
93
93
|
/** @readonly */
|
|
94
94
|
scan,
|
|
95
95
|
/** @readonly */
|
|
96
|
-
|
|
96
|
+
exclusiveScan,
|
|
97
97
|
/** @readonly */
|
|
98
98
|
flatMap,
|
|
99
99
|
/** @readonly */
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const op = require('../../function/operator')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @template T0
|
|
5
|
+
* @template T1
|
|
6
|
+
* @typedef {import('../array').Tuple2<T0, T1>} Tuple2
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @template T
|
|
11
|
+
* @template R
|
|
12
|
+
* @typedef {Tuple2<R, Scan<T, R>>} ScanResult
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @template T
|
|
17
|
+
* @template R
|
|
18
|
+
* @typedef {(value: T) => ScanResult<T, R>} Scan
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @template T
|
|
23
|
+
* @template R
|
|
24
|
+
* @typedef {Tuple2<R, Scan<T, R>>} ExclusiveScan
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/** @type {<R, T>(operator: op.ReduceOperator<R, T>) => (prior: R) => Scan<T, R>} */
|
|
28
|
+
const scan = operator => {
|
|
29
|
+
/** @typedef {typeof operator extends op.ReduceOperator<infer R, infer T> ? [R, T] : never} RT */
|
|
30
|
+
/** @typedef {RT[0]} R */
|
|
31
|
+
/** @typedef {RT[1]} T */
|
|
32
|
+
/** @type {(prior: R) => Scan<T, R>} */
|
|
33
|
+
const f = prior => value => {
|
|
34
|
+
const result = operator(prior)(value)
|
|
35
|
+
return [result, f(result)]
|
|
36
|
+
}
|
|
37
|
+
return f
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** @type {<R, T>(operator: op.ReduceOperator<R, T>) => (first: R) => ExclusiveScan<T, R>} */
|
|
41
|
+
const exclusiveScan = operator => first => [first, scan(operator)(first)]
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @template T
|
|
45
|
+
* @typedef {Tuple2<number, T>} Entry
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/** @type {(index: number) => <T>(value: T) => ScanResult<T, Entry<T>>} */
|
|
49
|
+
const createEntries = index => value => [[index, value], createEntries(index + 1)]
|
|
50
|
+
|
|
51
|
+
const entries = createEntries(0)
|
|
52
|
+
|
|
53
|
+
/** @type {(separator: string) => ExclusiveScan<string, string>} */
|
|
54
|
+
const join = separator => ['', value => [value, scan(op.join(separator))(value)]]
|
|
55
|
+
|
|
56
|
+
const sum = exclusiveScan(op.addition)(0)
|
|
57
|
+
|
|
58
|
+
/** @type {(a: number) => () => number} */
|
|
59
|
+
const counter = a => () => a + 1
|
|
60
|
+
|
|
61
|
+
const length = exclusiveScan(counter)(0)
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @template T
|
|
65
|
+
* @template R
|
|
66
|
+
* @typedef {(value: T) => R} Func
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
/** @type {<T, R, X>(flatMap: (f: Func<T, readonly[R]>) => X) => (f: Func<T, R>) =>X} */
|
|
70
|
+
const map = flatMap => f => flatMap(x => [f(x)])
|
|
71
|
+
|
|
72
|
+
/** @type {<T, X>(flatMap: (f: Func<T, readonly[T]|[]>) => X) => (f: Func<T, boolean>) =>X} */
|
|
73
|
+
const filter = flatMap => f => flatMap(x => f(x) ? [x] : [])
|
|
74
|
+
|
|
75
|
+
module.exports = {
|
|
76
|
+
/** @readonly */
|
|
77
|
+
exclusiveScan,
|
|
78
|
+
/** @readonly */
|
|
79
|
+
scan,
|
|
80
|
+
/** @readonly */
|
|
81
|
+
join,
|
|
82
|
+
/** @readonly */
|
|
83
|
+
sum,
|
|
84
|
+
/** @readonly */
|
|
85
|
+
length,
|
|
86
|
+
/** @readonly */
|
|
87
|
+
entries,
|
|
88
|
+
/** @readonly */
|
|
89
|
+
map,
|
|
90
|
+
/** @readonly */
|
|
91
|
+
filter,
|
|
92
|
+
}
|
package/sequence/test.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const seq = require('.')
|
|
2
|
+
const { sum } = require('./operator')
|
|
3
|
+
const op = require('./operator')
|
|
4
|
+
|
|
5
|
+
/** @type {<T>(input: seq.Sequence<T>) => void} */
|
|
6
|
+
const print = input => {
|
|
7
|
+
let i = input
|
|
8
|
+
while (true) {
|
|
9
|
+
const result = seq.next(i)
|
|
10
|
+
if (result === undefined) { return }
|
|
11
|
+
// console.log(result[0])
|
|
12
|
+
i = result[1]
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
{
|
|
17
|
+
const big = seq.fromArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 42, 60])
|
|
18
|
+
const list0 = seq.fromArray([0, 1, 2, 3])
|
|
19
|
+
const list1 = seq.flatMap(x => seq.fromArray([x, x * 2, x * 3]))(list0)
|
|
20
|
+
const list2 = seq.concat(list0, list0)
|
|
21
|
+
const list3 = seq.exclusiveScan(sum)(list0)
|
|
22
|
+
const r = seq.find(x => x === 42)(big)
|
|
23
|
+
if (seq.every(x => x > 0)(big) !== true) { throw 'x'}
|
|
24
|
+
if (seq.every(x => x < 20)(big) !== false) { throw 'x' }
|
|
25
|
+
if (seq.some(x => x > 100)(big) !== false) { throw 'x' }
|
|
26
|
+
if (seq.some(x => x > 50)(big) !== true) { throw 'x' }
|
|
27
|
+
if (seq.first(seq.drop(16)(big)) !== 42) { throw 'drop'}
|
|
28
|
+
{
|
|
29
|
+
let x = big
|
|
30
|
+
for (let i = 0; i < 1_000_000; ++i) {
|
|
31
|
+
// concat() still causes a stack overflow
|
|
32
|
+
x = seq.concat2(seq.empty)(x)
|
|
33
|
+
}
|
|
34
|
+
const r = seq.next(x)
|
|
35
|
+
// print(x)
|
|
36
|
+
}
|
|
37
|
+
{
|
|
38
|
+
let x = big
|
|
39
|
+
for (let i = 0; i < 1_000_000; ++i) {
|
|
40
|
+
// concat() still causes a stack overflow
|
|
41
|
+
x = seq.concat2(x)(seq.one(i))
|
|
42
|
+
}
|
|
43
|
+
const r = seq.next(x)
|
|
44
|
+
// print(x)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
{
|
|
49
|
+
const x = seq.join(':')(seq.fromArray(["1", "2", "3", "4", "5", "6"]))
|
|
50
|
+
if (x !== "1:2:3:4:5:6") { throw x }
|
|
51
|
+
}
|
package/test.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
const i = require('./')
|
|
2
2
|
|
|
3
|
-
require('./sequence/
|
|
3
|
+
require('./sequence/test')
|
|
4
4
|
require('./btree/test')
|
|
5
5
|
require('./sequence/iterable/test')
|
|
6
6
|
require('./sequence/asyncIterable/test')
|
|
7
7
|
require('./module-manager/test')
|
|
8
|
+
require('./json/test')
|
|
8
9
|
|
|
9
10
|
/** @type {() => never} */
|
|
10
11
|
const assert = () => { throw 'assert' }
|
package/sequence/list/index.js
DELETED
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
const array = require('../array')
|
|
2
|
-
const option = require('../../option')
|
|
3
|
-
const base = require('..')
|
|
4
|
-
const { pipe } = require('../../function')
|
|
5
|
-
const { todo } = require('../../dev')
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @template T
|
|
9
|
-
* @typedef {() => Result<T>} ListFunc
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @template T
|
|
14
|
-
* @typedef {readonly [List<T>, List<T>]} Concat
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Please note that the list also contains `Concat<T>. We need this as
|
|
19
|
-
* a workaround because modern JavaScript implementations don't support
|
|
20
|
-
* ES6 TCO (Tail Call Optimization).
|
|
21
|
-
*
|
|
22
|
-
* Without this wotkaround we may have a stack overflow if a list
|
|
23
|
-
* contains a lot of concateneted lists. Use `next` function to extract
|
|
24
|
-
* a list.
|
|
25
|
-
*
|
|
26
|
-
* @template T
|
|
27
|
-
* @typedef { ListFunc<T> | Concat<T>} List
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* @template T
|
|
32
|
-
* @typedef {FirstAndTail<T>|undefined} Result<T>
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* @template T
|
|
37
|
-
* @typedef {array.Tuple2<T, List<T>>} FirstAndTail
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
const empty = () => undefined
|
|
41
|
-
|
|
42
|
-
/** @type {<F, T>(a: readonly[F, List<T>]) => (b: List<T>) => readonly[F, List<T>]} */
|
|
43
|
-
const norm = ([a0, a1]) => b => [a0, [a1, b]]
|
|
44
|
-
|
|
45
|
-
/** @type {<T>(list: List<T>) => Result<T>} */
|
|
46
|
-
const next = list => {
|
|
47
|
-
let i = list
|
|
48
|
-
while (true) {
|
|
49
|
-
if (typeof i === 'function') { return i() }
|
|
50
|
-
const [a, b] = i
|
|
51
|
-
if (typeof a === 'function') {
|
|
52
|
-
const result = a()
|
|
53
|
-
if (result !== undefined) {
|
|
54
|
-
return norm(result)(b)
|
|
55
|
-
}
|
|
56
|
-
i = b
|
|
57
|
-
} else {
|
|
58
|
-
i = norm(a)(b)
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* @template T
|
|
65
|
-
* @template R
|
|
66
|
-
* @typedef {(list: List<T>) => List<R>} ListMap
|
|
67
|
-
*/
|
|
68
|
-
|
|
69
|
-
/** @type {<T>(first: T) => List<T>} */
|
|
70
|
-
const one = first => () => [first, empty]
|
|
71
|
-
|
|
72
|
-
/** @type {<T>(array: array.Array<T>) => List<T>} */
|
|
73
|
-
const fromArray = a => {
|
|
74
|
-
/** @typedef {typeof a extends array.Array<infer T> ? T : never} T */
|
|
75
|
-
/** @type {(index: number) => List<T>} */
|
|
76
|
-
const at = index => () => {
|
|
77
|
-
const result = array.at(index)(a)
|
|
78
|
-
if (result === undefined) { return undefined }
|
|
79
|
-
return [result[0], at(index + 1)]
|
|
80
|
-
}
|
|
81
|
-
return at(0)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** @type {<T>(list0: List<T>) => ListMap<T, T>} */
|
|
85
|
-
const concat = a => b => [a, b]
|
|
86
|
-
|
|
87
|
-
/** @type {<T, R>(f: (value: T) => List<R>) => ListMap<T, R>} */
|
|
88
|
-
const flatMap = f => input => () => {
|
|
89
|
-
let i = input
|
|
90
|
-
while (true) {
|
|
91
|
-
const result = next(i)
|
|
92
|
-
if (result === undefined) { return undefined }
|
|
93
|
-
const [first, tail] = result
|
|
94
|
-
const firstResult = next(f(first))
|
|
95
|
-
if (firstResult !== undefined) {
|
|
96
|
-
const [firstFirst, firstTail] = firstResult
|
|
97
|
-
return [firstFirst, concat(firstTail)(flatMap(f)(tail))]
|
|
98
|
-
}
|
|
99
|
-
i = tail
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** @type {<T>(list: List<List<T>>) => List<T>} */
|
|
104
|
-
const flat = flatMap(i => i)
|
|
105
|
-
|
|
106
|
-
/** @type {<T, R>(f: (value: T) => R) => ListMap<T, R>} */
|
|
107
|
-
const map = f => flatMap(i => one(f(i)))
|
|
108
|
-
|
|
109
|
-
/** @type {<T>(f: (value: T) => boolean) => ListMap<T, T>} */
|
|
110
|
-
const filter = f => flatMap(i => f(i) ? one(i) : empty)
|
|
111
|
-
|
|
112
|
-
/** @type {<T, R>(s: base.Scan<T, R>) => ListMap<T, R>} */
|
|
113
|
-
const scan = s => input => () => {
|
|
114
|
-
const result = next(input)
|
|
115
|
-
if (result === undefined) {
|
|
116
|
-
return result
|
|
117
|
-
}
|
|
118
|
-
const [first, tail] = result
|
|
119
|
-
const [newFirst, newS] = s(first)
|
|
120
|
-
return [newFirst, scan(newS)(tail)]
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/** @type {<T, R>(s: base.InclusiveScan<T, R>) => ListMap<T, R>} */
|
|
124
|
-
const inclusiveScan = ([first, s]) => input => () => [first, scan(s)(input)]
|
|
125
|
-
|
|
126
|
-
/** @type {<T>(def: T) => (input: List<T>) => T} */
|
|
127
|
-
const last = def => input => {
|
|
128
|
-
let r = def
|
|
129
|
-
let i = input
|
|
130
|
-
while (true) {
|
|
131
|
-
const result = next(i)
|
|
132
|
-
if (result === undefined) {
|
|
133
|
-
return r
|
|
134
|
-
}
|
|
135
|
-
r = result[0]
|
|
136
|
-
i = result[1]
|
|
137
|
-
}
|
|
138
|
-
return r
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** @type {<T, R>(s: base.InclusiveScan<T, R>) => (input: List<T>) => R} */
|
|
142
|
-
const reduce = ([first, s]) => input => last(first)(scan(s)(input))
|
|
143
|
-
|
|
144
|
-
const entries = scan(base.entries)
|
|
145
|
-
|
|
146
|
-
const sum = reduce(base.sum)
|
|
147
|
-
|
|
148
|
-
const length = reduce(base.length)
|
|
149
|
-
|
|
150
|
-
const join = pipe(base.join)(reduce)
|
|
151
|
-
|
|
152
|
-
/** @type {<T>(f: (value: T) => boolean) => ListMap<T, T>} */
|
|
153
|
-
const takeWhile = f => input => () => {
|
|
154
|
-
const result = next(input)
|
|
155
|
-
if (result === undefined || !f(result[0])) { return undefined }
|
|
156
|
-
return result
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/** @type {<T>(f: (value: T) => boolean) => (input: List<T>) => T|undefined} */
|
|
160
|
-
const find = f => input => {
|
|
161
|
-
const result = next(filter(f)(input))
|
|
162
|
-
if (result === undefined) { return undefined }
|
|
163
|
-
return result[0]
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Note: probably, it's possible to implement using the `scan` concept.
|
|
168
|
-
* @type {<T>(list: List<T>) => Iterable<T>}
|
|
169
|
-
*/
|
|
170
|
-
const iterable = list => ({
|
|
171
|
-
*[Symbol.iterator]() {
|
|
172
|
-
let i = list
|
|
173
|
-
while (true) {
|
|
174
|
-
const result = next(i)
|
|
175
|
-
if (result === undefined) { return }
|
|
176
|
-
yield result[0]
|
|
177
|
-
i = result[1]
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
module.exports = {
|
|
183
|
-
/** @readonly */
|
|
184
|
-
next,
|
|
185
|
-
/** @readonly */
|
|
186
|
-
one,
|
|
187
|
-
/** @readonly */
|
|
188
|
-
empty,
|
|
189
|
-
/** @readonly */
|
|
190
|
-
concat,
|
|
191
|
-
/** @readonly */
|
|
192
|
-
fromArray,
|
|
193
|
-
/** @readonly */
|
|
194
|
-
iterable,
|
|
195
|
-
/** @readonly */
|
|
196
|
-
flatMap,
|
|
197
|
-
/** @readonly */
|
|
198
|
-
flat,
|
|
199
|
-
/** @readonly */
|
|
200
|
-
map,
|
|
201
|
-
/** @readonly */
|
|
202
|
-
filter,
|
|
203
|
-
/** @readonly */
|
|
204
|
-
scan,
|
|
205
|
-
/** @readonly */
|
|
206
|
-
inclusiveScan,
|
|
207
|
-
/** @readonly */
|
|
208
|
-
last,
|
|
209
|
-
/** @readonly */
|
|
210
|
-
reduce,
|
|
211
|
-
/** @readonly */
|
|
212
|
-
entries,
|
|
213
|
-
/** @readonly */
|
|
214
|
-
sum,
|
|
215
|
-
/** @readonly */
|
|
216
|
-
join,
|
|
217
|
-
/** @readonly */
|
|
218
|
-
length,
|
|
219
|
-
/** @readonly */
|
|
220
|
-
find,
|
|
221
|
-
/** @readonly */
|
|
222
|
-
takeWhile,
|
|
223
|
-
}
|
package/sequence/list/test.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const list = require('.')
|
|
2
|
-
const { sum } = require('..')
|
|
3
|
-
|
|
4
|
-
/** @type {<T>(l: list.List<T>) => void} */
|
|
5
|
-
const print = a => {
|
|
6
|
-
let i = a
|
|
7
|
-
while (true) {
|
|
8
|
-
const result = list.next(i)
|
|
9
|
-
if (result === undefined) { return }
|
|
10
|
-
console.log(result[0])
|
|
11
|
-
i = result[1]
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
{
|
|
16
|
-
const big = list.fromArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 42, 60])
|
|
17
|
-
const list0 = list.fromArray([0, 1, 2, 3])
|
|
18
|
-
const list1 = list.flatMap(x => list.fromArray([x, x * 2, x * 3]))(list0)
|
|
19
|
-
const list2 = list.concat(list0)(list0)
|
|
20
|
-
const list3 = list.inclusiveScan(sum)(list0)
|
|
21
|
-
const r = list.find(x => x === 42)(big)
|
|
22
|
-
{
|
|
23
|
-
let x = big
|
|
24
|
-
for (let i = 0; i < 1_000_000; ++i) {
|
|
25
|
-
x = list.concat(list.empty)(x)
|
|
26
|
-
}
|
|
27
|
-
const r = list.next(x)
|
|
28
|
-
print(x)
|
|
29
|
-
}
|
|
30
|
-
{
|
|
31
|
-
let x = big
|
|
32
|
-
for (let i = 0; i < 1_000_000; ++i) {
|
|
33
|
-
x = list.concat(x)(list.one(i))
|
|
34
|
-
}
|
|
35
|
-
const r = list.next(x)
|
|
36
|
-
// print(x)
|
|
37
|
-
}
|
|
38
|
-
}
|