Skip to content

Commit 076ee35

Browse files
🚴 perf(iterator/reversed): 5-6x faster iterators.
This also has the benefit of reducing legacy bundle sizes.
1 parent 3dbdfb8 commit 076ee35

File tree

10 files changed

+170
-39
lines changed

10 files changed

+170
-39
lines changed

src/0-core/_fast/fast-iterators/1-_EmptyGenerator.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ export function _EmptyGenerator() {}
33
_EmptyGenerator.prototype.next = function () {
44
return {done: true};
55
};
6+
7+
_EmptyGenerator.prototype[Symbol.iterator] = function () {
8+
return this;
9+
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import assert from 'assert';
2+
import {Node2} from '../../../2-node/2-Node2.js';
3+
import {Node3} from '../../../2-node/3-Node3.js';
4+
import {Deep} from '../../../3-tree/implementations/2-Deep.js';
5+
import DeepIterator, {DOWNWARD} from './DeepIterator.js';
6+
7+
/**
8+
* BackwardIterator.
9+
*
10+
* @param {Deep} tree
11+
*/
12+
export default function BackwardIterator(tree) {
13+
assert(tree instanceof Deep);
14+
this._stack = tree._right._list();
15+
this._level = this._stack.map(() => 0);
16+
this._tree = tree;
17+
this._direction = DOWNWARD;
18+
this._currentLevel = 0;
19+
this._treeStack = [];
20+
}
21+
22+
BackwardIterator.prototype = new DeepIterator();
23+
24+
BackwardIterator.prototype._downwardStep = function () {
25+
this._stack = this._tree._right._list();
26+
};
27+
28+
BackwardIterator.prototype._upwardStep = function () {
29+
this._stack = this._tree._left._list();
30+
};
31+
32+
BackwardIterator.prototype._traverse = function (level, x) {
33+
if (x instanceof Node3) {
34+
this._level.push(level, level);
35+
this._stack.push(x.a, x.b);
36+
return x.c;
37+
}
38+
39+
assert(x instanceof Node2);
40+
this._level.push(level);
41+
this._stack.push(x.a);
42+
return x.b;
43+
};
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import assert from 'assert';
2+
import {Empty} from '../../../3-tree/implementations/0-Empty.js';
3+
import {Single} from '../../../3-tree/implementations/1-Single.js';
4+
import {Deep} from '../../../3-tree/implementations/2-Deep.js';
5+
6+
export const DOWNWARD = 0;
7+
export const UPWARD = 1;
8+
9+
export default function DeepIterator() {}
10+
11+
DeepIterator.prototype._pop = function () {
12+
assert(this._stack.length >= 1);
13+
assert(this._stack.length === this._level.length);
14+
let x = this._stack.pop();
15+
let level = this._level.pop();
16+
17+
for (;;) {
18+
assert(this._stack.length === this._level.length);
19+
assert(Number.isInteger(level) && level >= 0);
20+
if (level === 0) return {done: false, value: x};
21+
22+
x = this._traverse(--level, x);
23+
}
24+
};
25+
26+
DeepIterator.prototype.next = function () {
27+
if (this._stack.length === 0) {
28+
/* eslint-disable no-fallthrough */
29+
// eslint-disable-next-line default-case
30+
switch (this._direction) {
31+
case DOWNWARD:
32+
this._tree._middle = this._tree._middle._force();
33+
if (this._tree._middle instanceof Deep) {
34+
this._treeStack.push(this._tree);
35+
this._tree = this._tree._middle;
36+
this._downwardStep();
37+
++this._currentLevel;
38+
this._level = this._stack.map(() => this._currentLevel);
39+
break;
40+
} else {
41+
this._direction = UPWARD;
42+
if (this._tree._middle instanceof Single) {
43+
this._stack = [this._tree._middle.a];
44+
this._level = [this._currentLevel + 1];
45+
break;
46+
}
47+
48+
assert(this._tree._middle instanceof Empty);
49+
}
50+
51+
case UPWARD:
52+
if (this._currentLevel === -1) return {done: true};
53+
assert(this._tree instanceof Deep);
54+
this._upwardStep();
55+
this._level = this._stack.map(() => this._currentLevel);
56+
57+
this._tree = --this._currentLevel === -1 ? null : this._treeStack.pop();
58+
}
59+
/* eslint-enable no-fallthrough */
60+
}
61+
62+
return this._pop();
63+
};
64+
65+
DeepIterator.prototype[Symbol.iterator] = function () {
66+
return this;
67+
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import assert from 'assert';
2+
import {Node2} from '../../../2-node/2-Node2.js';
3+
import {Node3} from '../../../2-node/3-Node3.js';
4+
import {Deep} from '../../../3-tree/implementations/2-Deep.js';
5+
import DeepIterator, {DOWNWARD} from './DeepIterator.js';
6+
7+
/**
8+
* ForwardIterator.
9+
*
10+
* @param {Deep} tree
11+
*/
12+
export default function ForwardIterator(tree) {
13+
assert(tree instanceof Deep);
14+
this._stack = tree._left._list().reverse();
15+
this._level = this._stack.map(() => 0);
16+
this._tree = tree;
17+
this._direction = DOWNWARD;
18+
this._currentLevel = 0;
19+
this._treeStack = [];
20+
}
21+
22+
ForwardIterator.prototype = new DeepIterator();
23+
24+
ForwardIterator.prototype._downwardStep = function () {
25+
this._stack = this._tree._left._list().reverse();
26+
};
27+
28+
ForwardIterator.prototype._upwardStep = function () {
29+
this._stack = this._tree._right._list().reverse();
30+
};
31+
32+
ForwardIterator.prototype._traverse = function (level, x) {
33+
if (x instanceof Node3) {
34+
this._level.push(level, level);
35+
this._stack.push(x.c, x.b);
36+
return x.a;
37+
}
38+
39+
assert(x instanceof Node2);
40+
this._level.push(level);
41+
this._stack.push(x.b);
42+
return x.a;
43+
};

src/1-digit/0-Digit.js

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1 @@
11
export function Digit() {}
2-
3-
Digit.prototype[Symbol.iterator] = function () {
4-
return this._list()[Symbol.iterator]();
5-
};
6-
7-
Digit.prototype.reversed = function () {
8-
return this._list().reverse()[Symbol.iterator]();
9-
};

src/2-node/2-Node2.js

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,6 @@ export function Node2(v, a, b) {
66
this.b = b;
77
}
88

9-
Node2.prototype[Symbol.iterator] = function () {
10-
return [this.a, this.b][Symbol.iterator]();
11-
};
12-
13-
Node2.prototype.reversed = function () {
14-
return [this.b, this.a][Symbol.iterator]();
15-
};
16-
179
Node2.prototype.measure = function () {
1810
return this.v;
1911
};

src/2-node/3-Node3.js

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,6 @@ export function Node3(v, a, b, c) {
77
this.c = c;
88
}
99

10-
Node3.prototype[Symbol.iterator] = function () {
11-
return [this.a, this.b, this.c][Symbol.iterator]();
12-
};
13-
14-
Node3.prototype.reversed = function () {
15-
return [this.c, this.b, this.a][Symbol.iterator]();
16-
};
17-
1810
Node3.prototype.measure = function () {
1911
return this.v;
2012
};

src/3-tree/implementations/0-Empty.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ Empty.prototype.concat = function (other) {
5959
return other;
6060
};
6161

62-
Empty.prototype[Symbol.iterator] = function () {
63-
return _EMPTY;
64-
};
65-
66-
Empty.prototype.reversed = function* () {};
62+
Empty.prototype[Symbol.iterator] =
63+
// eslint-disable-next-line no-multi-assign
64+
Empty.prototype.reversed = function () {
65+
return _EMPTY;
66+
};
6767

6868
/**
6969
* It is assumed that p(i+|this|) is true.

src/3-tree/implementations/1-Single.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ Single.prototype.concat = function (other) {
7676

7777
Single.prototype[Symbol.iterator] =
7878
// eslint-disable-next-line no-multi-assign
79-
Single.prototype.reversed = function* () {
80-
yield this.a;
79+
Single.prototype.reversed = function () {
80+
return [this.a][Symbol.iterator]();
8181
};
8282

8383
/**

src/3-tree/implementations/2-Deep.js

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import isSameMeasure from '../../_debug/isSameMeasure.js';
88
import _from_by_filling from '../../0-core/_fast/_from_by_filling.js';
99
import empty from '../../5-api/empty.js';
1010
import delayApp3RecurseStep from '../../4-lazy/delayApp3RecurseStep.js';
11+
import ForwardIterator from '../../0-core/_fast/fast-iterators/ForwardIterator.js';
12+
import BackwardIterator from '../../0-core/_fast/fast-iterators/BackwardIterator.js';
1113
import {Empty} from './0-Empty.js';
1214
import {Single} from './1-Single.js';
1315

@@ -98,16 +100,12 @@ Deep.prototype.concat = function (other) {
98100
return other._concat_with_deep(this);
99101
};
100102

101-
Deep.prototype[Symbol.iterator] = function* () {
102-
yield* this._left;
103-
for (const node of this._middle) yield* node;
104-
yield* this._right;
103+
Deep.prototype[Symbol.iterator] = function () {
104+
return new ForwardIterator(this);
105105
};
106106

107-
Deep.prototype.reversed = function* () {
108-
yield* this._right.reversed();
109-
for (const node of this._middle.reversed()) yield* node.reversed();
110-
yield* this._left.reversed();
107+
Deep.prototype.reversed = function () {
108+
return new BackwardIterator(this);
111109
};
112110

113111
/**

0 commit comments

Comments
 (0)