-
David Sehnal authoredDavid Sehnal authored
iterator.ts 3.24 KiB
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
/**
* "Idiomatic" usage:
*
* const it = ...;
* while (it.hasNext) { const v = it.move(); ... }
*/
interface Iterator<T> {
readonly hasNext: boolean,
move(): T
}
class ArrayIteratorImpl<T> implements Iterator<T> {
private xs: ArrayLike<T> = [];
private index: number = -1;
private length: number = 0;
private lastValue: T;
hasNext: boolean = false;
move() {
++this.index;
this.lastValue = this.xs[this.index];
this.hasNext = this.index < this.length - 1;
return this.lastValue;
}
constructor(xs: ArrayLike<T>) {
this.length = xs.length;
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;
}
}
class RangeIteratorImpl implements Iterator<number> {
private value: number = 0;
hasNext: boolean = false;
move() {
++this.value;
this.hasNext = this.value < this.max;
return this.value;
}
constructor(min: number, private max: number) {
this.value = min - 1;
this.hasNext = max >= min;
}
}
class ValueIterator<T> implements Iterator<T> {
hasNext = true;
move() { this.hasNext = false; return this.value; }
constructor(private value: T) { }
}
class MapIteratorImpl<T, R> implements Iterator<R> {
hasNext: boolean = false;
move() {
const v = this.f(this.base.move());
this.hasNext = this.base.hasNext;
return v;
}
constructor(private base: Iterator<T>, private f: (v: T) => R) {
this.hasNext = base.hasNext;
}
}
class FilterIteratorImpl<T> implements Iterator<T> {
private next: T;
hasNext: boolean;
move() {
const ret = this.next;
this.hasNext = this.findNext();
return ret;
}
private findNext() {
while (this.base.hasNext) {
this.next = this.base.move();
if (this.p(this.next)) return true;
}
return false;
}
constructor(private base: Iterator<T>, private p: (v: T) => boolean) {
this.hasNext = this.findNext();
}
}
namespace Iterator {
export const Empty: Iterator<any> = new RangeIteratorImpl(0, -1);
export function Array<T>(xs: ArrayLike<T>): Iterator<T> { return new ArrayIteratorImpl<T>(xs); }
export function Value<T>(value: T): Iterator<T> { return new ValueIterator(value); }
export function Range(min: number, max: number): Iterator<number> { return new RangeIteratorImpl(min, max); }
export function map<T, R>(base: Iterator<T>, f: (v: T) => R): Iterator<R> { return new MapIteratorImpl(base, f); }
export function filter<T>(base: Iterator<T>, p: (v: T) => boolean): Iterator<T> { return new FilterIteratorImpl(base, p); }
// Iterate until first truthy value is returned.
export function forEach<T, Ctx>(it: Iterator<T>, f: (v: T, ctx: Ctx) => any, ctx: Ctx): Ctx {
while (it.hasNext) {
const c = f(it.move(), ctx);
if (c) return ctx;
}
return ctx;
}
}
export default Iterator