// // ----- Sequences ------------------------- // const range = (l, u = Infinity) => function* () { while (true) { if (l < u) { yield l++; continue; } return l; } }; const repeat = (n) => function* () { while (true) { yield n; } }; const fibonacci = () => function* () { let f0 = 1; let f1 = 1; yield f0; yield f1; while (true) { [f0, f1] = [f1, f0 + f1]; yield f1; } }; const primes = () => function* () { const ps = []; let n = 2; while (true) { const filteredPs = ps.filter( x => x < Math.ceil(Math.sqrt(n)) ); if (filteredPs.some(p => (n % p) === 0)) { n++; continue; } ps.push(n); yield n++; } }; // // ----- Utility functions ------------------------- // const $SKIP = Symbol('skip'); const take = (n) => (generator) => function* () { while (n-- > 0) { const { value, done } = generator.next(); if (done) { return value; } yield value; } }; const takeWhile = (fn) => (generator) => function* () { while (true) { const { value, done } = generator.next(); if (done && fn(value)) { return value; } if (done && !fn(value)) { return null; } if (fn(value)) { yield value; } if (!fn(value)) { return null; } } }; const drop = (n) => (generator) => function* () { let counter = 0; while (++counter <= n) { const { done } = generator.next(); if (done) { return null; } } while (true) { const { value, done } = generator.next(); if (done) { return value; } yield value; } }; const dropWhile = (fn) => (generator) => function* () { while (true) { const { value, done } = generator.next(); if (done && fn(value)) { return null; } if (done && !fn(value)) { return value; } if (fn(value)) { continue; } yield value; } }; const map = (fn) => (generator) => function* () { while (true) { const { value, done } = generator.next(); if (done) { return fn(value); } yield fn(value); } }; const filter = (fn) => (generator) => function* () { while (true) { const { value, done } = generator.next(); if (done && fn(value)) { return value; } if (done && !fn(value)) { return $SKIP; } if (!fn(value)) { continue; } yield value; } }; // // ----- Lazy list ------------------------- // class LazyList { #generator; #generatedValues = []; #done = false; constructor(generatorFn) { this.#generator = generatorFn(); } #next() { const { value, done } = this.#generator.next(); if (done) { this.#done = true; } if (value !== $SKIP) { this.#generatedValues.push(value); } } #asGenerator() { function* toGenerator() { let cursor = 0; while (true) { if (!Reflect.has(this.#generatedValues, cursor)) { this.#next(); } if (this.#done) { return this.#generatedValues[cursor++]; } yield this.#generatedValues[cursor++]; } } return toGenerator.apply(this); } get(n) { while (this.#generatedValues.length <= n && !this.#done) { this.#next(); } return this.#generatedValues[n]; } take(n) { const generator = take(n)(this.#asGenerator()); return new LazyList(generator); } takeWhile(fn) { const generator = takeWhile(fn)(this.#asGenerator()); return new LazyList(generator); } drop(n) { const generator = drop(n)(this.#asGenerator()); return new LazyList(generator); } dropWhile(fn) { const generator = dropWhile(fn)(this.#asGenerator()); return new LazyList(generator); } map(fn) { const generator = map(fn)(this.#asGenerator()); return new LazyList(generator); } filter(fn) { const generator = filter(fn)(this.#asGenerator()); return new LazyList(generator); } }