Browse Source

simplified iterators

David Sehnal 7 years ago
parent
commit
d48e496c5f
2 changed files with 31 additions and 23 deletions
  1. 1 1
      src/structure/collections/int-pair.ts
  2. 30 22
      src/structure/collections/iterator.ts

+ 1 - 1
src/structure/collections/int-pair.ts

@@ -28,4 +28,4 @@ namespace IntPair {
     }
 }
 
-export default IntPair
+export default IntPair

+ 30 - 22
src/structure/collections/iterator.ts

@@ -5,64 +5,72 @@
  */
 
 /**
- * ES6 compatible mutable iterator. Use with care. 
+ * ES6 compatible iterator.
  *
- * "Idiomatic" usage:
+ * "Idiomatic" usage is to use the move function, because it does not make any allocations.
  *
  * const it = ...;
- * for (let v = it.nextValue(); !it.done; v = it.nextValue()) { ... }
+ * for (let v = it.move(); it.hasNext; v = it.move()) { ... }
  */
 interface Iterator<T> {
     [Symbol.iterator](): Iterator<T>,
-    readonly done: boolean,
-    readonly value: T,
+    readonly hasNext: boolean,
     next(): { done: boolean, value: T },
-    nextValue(): T
+    move(): T
 }
 
 class ArrayIteratorImpl<T> implements Iterator<T> {
     private xs: ArrayLike<T> = [];
     private index: number = -1;
     private length: number = 0;
+    private lastValue: T;
 
     [Symbol.iterator]() { return this; };
-    done = true;
-    value: T = void 0 as any;
+    hasNext: boolean;
 
     next() {
-        const index = ++this.index;
-        if (index < this.length) this.value = this.xs[index];
-        else this.done = true;
-        return this;
+        const value = this.move();
+        return { value, done: !this.hasNext };
     }
 
-    nextValue() { return this.next().value; }
+    move() {
+        const index = ++this.index;
+        if (index < this.length) this.lastValue = this.xs[index];
+        else this.hasNext = false;
+        return this.lastValue;
+    }
 
     constructor(xs: ArrayLike<T>) {
         this.length = xs.length;
-        this.done = false;
+        this.hasNext = xs.length > 0;
         this.xs = xs;
         this.index = -1;
+        // try to avoid deoptimization with undefined values
+        this.lastValue = xs.length > 0 ? xs[0] : void 0 as any;
         return this;
     }
 }
 
 class RangeIteratorImpl implements Iterator<number> {
+    private value: number;
+
     [Symbol.iterator]() { return this; };
-    done = true;
-    value: number;
+    hasNext: boolean;
 
     next() {
-        ++this.value;
-        this.done = this.value > this.max;
-        return this;
+        const value = this.value;
+        return { value, done: !this.hasNext }
     }
 
-    nextValue() { return this.next().value;  }
+    move() {
+        ++this.value;
+        this.hasNext = this.value <= this.max;
+        return this.value;
+    }
 
     constructor(min: number, private max: number) {
         this.value = min - 1;
-        this.done = false;
+        this.hasNext = max >= min;
         return this;
     }
 }
@@ -75,7 +83,7 @@ namespace Iterator {
 
     export function toArray<T>(it: Iterator<T>): T[] {
         const ret = [];
-        for (let v = it.nextValue(); !it.done; v = it.nextValue()) ret[ret.length] = v;
+        for (let v = it.move(); it.hasNext; v = it.move()) ret[ret.length] = v;
         return ret;
     }
 }