Skip to content

Instantly share code, notes, and snippets.

@ryandabler
Last active April 5, 2022 13:43
Show Gist options
  • Save ryandabler/f4f19256183bec068bcb4e2094678f5f to your computer and use it in GitHub Desktop.
Save ryandabler/f4f19256183bec068bcb4e2094678f5f to your computer and use it in GitHub Desktop.
//
// ----- 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);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment