diff --git a/website/src/playground/files/root/examples/useRange.ts b/website/src/playground/files/root/examples/useRange.ts new file mode 100644 index 00000000..57c40bdf --- /dev/null +++ b/website/src/playground/files/root/examples/useRange.ts @@ -0,0 +1,23 @@ +// This program solves https://projecteuler.net/problem=2 using the `Range` +// library. Range provides operations on iterables that are very similar to the +// higher order functions on arrays. By using iterables, you can work with +// infinite sequences (eg fibonacci), and the processing is done incrementally +// instead of serializing each step into memory. + +import { Range } from "../lib/mod.ts"; + +export default function main() { + return Range.from(fibonacci()) + .while((x) => x < 4_000_000) + .filter((x) => x % 2 === 0) + .sum(); +} + +function* fibonacci() { + let [fibLast, fib] = [0, 1]; + + while (true) { + yield fib; + [fibLast, fib] = [fib, fibLast + fib]; + } +} diff --git a/website/src/playground/files/root/lib/Range.ts b/website/src/playground/files/root/lib/Range.ts new file mode 100644 index 00000000..7f09b712 --- /dev/null +++ b/website/src/playground/files/root/lib/Range.ts @@ -0,0 +1,423 @@ +import { primes } from "./primes.ts"; + +export default class Range implements Iterable { + iterable: Iterable; + + constructor(iterable: Iterable) { + this.iterable = iterable; + } + + [Symbol.iterator]() { + return this.iterable[Symbol.iterator](); + } + + limit(n: number) { + const iterable = this.iterable; + + function* res() { + let i = 0; + + for (const x of iterable) { + if (i >= n) { + break; + } + + yield x; + i++; + } + } + + return new Range(res()); + } + + count() { + let i = 0; + + for (const _x of this.iterable) { + i++; + } + + return i; + } + + empty() { + for (const _x of this.iterable) { + return false; + } + + return true; + } + + stringJoin(sep = "") { + let iter = this[Symbol.iterator](); + + const first = iter.next(); + + if (first.done) { + return ""; + } + + let res = String(first.value); + + for (const x of asIterable(iter)) { + res += sep; + res += x; + } + + return res; + } + + sum( + // Warning: ValueScript has a bug where typing the `this` parameter causes it to create a + // phantom regular parameter. This only works because there aren't any other parameters. + // TODO: Fix this. + this: Range, + ) { + let res = 0; + + for (const x of this.iterable) { + res += x; + } + + return res; + } + + bigSum( + // Warning: ValueScript has a bug where typing the `this` parameter causes it to create a + // phantom regular parameter. This only works because there aren't any other parameters. + // TODO: Fix this. + this: Range, + ) { + let res = 0n; + + for (const x of this.iterable) { + res += x; + } + + return res; + } + + product(this: Range) { + let res = 1; + + for (const x of this.iterable) { + res *= x; + } + + return res; + } + + bigProduct( + // Warning: ValueScript has a bug where typing the `this` parameter causes it to create a + // phantom regular parameter. This only works because there aren't any other parameters. + // TODO: Fix this. + this: Range, + ) { + let res = 1n; + + for (const x of this.iterable) { + res *= x; + } + + return res; + } + + map(fn: (x: T) => MappedT) { + const iterable = this.iterable; + + function* res() { + for (const x of iterable) { + yield fn(x); + } + } + + return new Range(res()); + } + + flatMap(fn: (x: T) => Iterable) { + const iterable = this.iterable; + + function* res() { + for (const x of iterable) { + for (const y of fn(x)) { + yield y; + } + } + } + + return new Range(res()); + } + + flatten(this: Range>) { + const iterable = this.iterable; + + function* res() { + for (const x of iterable) { + for (const y of x) { + yield y; + } + } + } + + return new Range(res()); + } + + filter(fn: (x: T) => boolean) { + const iterable = this.iterable; + + function* res() { + for (const x of iterable) { + if (fn(x)) { + yield x; + } + } + } + + return new Range(res()); + } + + // TODO: Negative indexes + at(n: number) { + let i = 0; + + for (const x of this.iterable) { + if (i === n) { + return x; + } + + i++; + } + } + + first() { + for (const x of this.iterable) { + return x; + } + } + + last() { + let res: T | undefined; + + for (const x of this.iterable) { + res = x; + } + + return res; + } + + indexed() { + const iterable = this.iterable; + + function* res() { + let i = 0; + + for (const x of iterable) { + yield [i, x] as [number, T]; + i++; + } + } + + return new Range(res()); + } + + append(newItems: Iterable) { + const iterable = this.iterable; + + function* res() { + yield* iterable; + yield* newItems; + } + + return new Range(res()); + } + + prepend(newItems: Iterable) { + const iterable = this.iterable; + + function* res() { + yield* newItems; + yield* iterable; + } + + return new Range(res()); + } + + zip(other: Iterable) { + const iterable = this.iterable; + + function* res() { + let iter1 = iterable[Symbol.iterator](); + let iter2 = other[Symbol.iterator](); + + while (true) { + const x1 = iter1.next(); + const x2 = iter2.next(); + + if (x1.done || x2.done) { + break; + } + + yield [x1.value, x2.value] as [T, U]; + } + } + + return new Range(res()); + } + + skip(n: number) { + const iterable = this.iterable; + + function* res() { + let iter = iterable[Symbol.iterator](); + + for (let i = 0; i < n; i++) { + iter.next(); + } + + while (true) { + const x = iter.next(); + + if (x.done) { + break; + } + + yield x.value; + } + } + + return new Range(res()); + } + + reduce(state: S, fn: (state: S, x: T) => S) { + for (const x of this.iterable) { + state = fn(state, x); + } + + return state; + } + + while(fn: (x: T) => boolean) { + const iterable = this.iterable; + + function* res() { + for (const x of iterable) { + if (fn(x)) { + yield x; + } else { + break; + } + } + } + + return new Range(res()); + } + + window(len: number) { + const iterable = this.iterable; + + function* res() { + let iter = iterable[Symbol.iterator](); + let memory = []; + + for (let i = 0; i < len; i++) { + const { value, done } = iter.next(); + + if (done) { + return; + } + + memory.push(value); + } + + yield new Range(memory); + + let i = 0; + + for (const x of asIterable(iter)) { + memory[i] = x; + + const memoryCopy = memory; + const iCopy = i; + + yield new Range((function* () { + for (let j = 1; j <= len; j++) { + yield memoryCopy[(iCopy + j) % len]; + } + })()); + + i++; + i %= len; + } + } + + return new Range(res()); + } + + static fromConversion( + iter?: Iterable | Iterator | (() => Iterable), + ) { + if (iter === undefined) { + return new Range([]); + } + + if (typeof iter === "function") { + return new Range(iter()); + } + + // TODO: `in` operator + if (hasKey(iter, Symbol.iterator)) { + return new Range(iter); + } + + if (hasKey(iter, "next")) { + return Range.fromIterator(iter); + } + + never(iter); + } + + static from(iterable: Iterable = []) { + return new Range(iterable); + } + + static fromIterator(iterator: Iterator) { + return new Range({ + [Symbol.iterator]: () => iterator, + }); + } + + static numbers(start = 0, end?: number) { + if (end === undefined) { + return new Range((function* () { + for (let i = start;; i++) { + yield i; + } + })()); + } + + return new Range((function* () { + for (let i = start; i < end; i++) { + yield i; + } + })()); + } + + static primes() { + return new Range(primes()); + } +} + +function hasKey( + obj: unknown, + key: K, +): obj is Obj & Record { + return (obj as Record)[key] !== undefined; +} + +function never(x: never): never { + throw new Error(`Unexpected value: ${x}`); +} + +function asIterable(iterator: Iterator): Iterable { + return { [Symbol.iterator]: () => iterator }; +} diff --git a/website/src/playground/files/root/lib/mod.ts b/website/src/playground/files/root/lib/mod.ts index 6c6af63f..bea25da6 100644 --- a/website/src/playground/files/root/lib/mod.ts +++ b/website/src/playground/files/root/lib/mod.ts @@ -1,6 +1,6 @@ export { default as BinaryTree } from "./BinaryTree.ts"; export { type NotNullish } from "./util.ts"; -export { factorize, factorizeAsPowers, primes, isPrime } from "./primes.ts"; -export { default as range } from "./range.ts"; +export { factorize, factorizeAsPowers, isPrime, primes } from "./primes.ts"; +export { default as Range } from "./Range.ts"; // Note: `export *` would be used here, but it isn't implemented yet. diff --git a/website/src/playground/files/root/lib/range.ts b/website/src/playground/files/root/lib/range.ts deleted file mode 100644 index 9b6e065f..00000000 --- a/website/src/playground/files/root/lib/range.ts +++ /dev/null @@ -1,312 +0,0 @@ -import { primes } from "./primes.ts"; - -export default function range(iterable: Iterable) { - return new Range(iterable); -} - -// TODO: Static methods -export function Range_from( - iter?: Iterable | Iterator | (() => Iterable), -) { - if (iter === undefined) { - return Range_fromIterable([]); - } - - if (typeof iter === "function") { - return Range_fromIterable(iter()); - } - - // TODO: `in` operator - if (hasKey(iter, Symbol.iterator)) { - return Range_fromIterable(iter); - } - - if (hasKey(iter, "next")) { - return Range_fromIterator(iter); - } - - never(iter); -} - -export function Range_fromIterable(iterable: Iterable = []) { - return new Range(iterable); -} - -export function Range_fromIterator(iterator: Iterator) { - return new Range({ - [Symbol.iterator]: () => iterator, - }); -} - -export function Range_numbers(start: number, end: number) { - function* res() { - for (let i = start; i < end; i++) { - yield i; - } - } - - return Range_fromIterable(res()); -} - -export function Range_primes() { - return new Range(primes()); -} - -export class Range implements Iterable { - iterable: Iterable; - - constructor(iterable: Iterable) { - this.iterable = iterable; - } - - [Symbol.iterator]() { - return this.iterable[Symbol.iterator](); - } - - limit(n: number) { - const iterable = this.iterable; - - function* res() { - let i = 0; - - for (const x of iterable) { - if (i >= n) { - break; - } - - yield x; - i++; - } - } - - return Range_fromIterable(res()); - } - - count() { - let i = 0; - - for (const _x of this.iterable) { - i++; - } - - return i; - } - - empty() { - for (const _x of this.iterable) { - return false; - } - - return true; - } - - stringJoin(sep = "") { - let iter = this[Symbol.iterator](); - - const first = iter.next(); - - if (first.done) { - return ""; - } - - let res = String(first.value); - - for (const x of this.iterable) { - res += sep; - res += x; - } - - return res; - } - - sum(): T extends number ? number : never { - let res = 0; - - for (const x of this.iterable) { - res += x as number; - } - - return res as T extends number ? number : never; - } - - product(): T extends number ? number : never { - let res = 1; - - for (const x of this.iterable) { - res *= x as number; - } - - return res as T extends number ? number : never; - } - - map(fn: (x: T) => MappedT) { - const iterable = this.iterable; - - function* res() { - for (const x of iterable) { - yield fn(x); - } - } - - return Range_fromIterable(res()); - } - - flatMap(fn: (x: T) => Iterable) { - const iterable = this.iterable; - - function* res() { - for (const x of iterable) { - for (const y of fn(x)) { - yield y; - } - } - } - - return Range_fromIterable(res()); - } - - filter(fn: (x: T) => boolean) { - const iterable = this.iterable; - - function* res() { - for (const x of iterable) { - if (fn(x)) { - yield x; - } - } - } - - return Range_fromIterable(res()); - } - - // TODO: Negative indexes - at(n: number) { - let i = 0; - - for (const x of this.iterable) { - if (i === n) { - return x; - } - - i++; - } - } - - first() { - for (const x of this.iterable) { - return x; - } - } - - last() { - let res: T | undefined; - - for (const x of this.iterable) { - res = x; - } - - return res; - } - - indexed() { - const iterable = this.iterable; - - function* res() { - let i = 0; - - for (const x of iterable) { - yield [i, x] as [number, T]; - i++; - } - } - - return Range_fromIterable(res()); - } - - append(newItems: Iterable) { - const iterable = this.iterable; - - function* res() { - yield* iterable; - yield* newItems; - } - - return Range_fromIterable(res()); - } - - prepend(newItems: Iterable) { - const iterable = this.iterable; - - function* res() { - yield* newItems; - yield* iterable; - } - - return Range_fromIterable(res()); - } - - zip(other: Iterable) { - const iterable = this.iterable; - - function* res() { - let iter1 = iterable[Symbol.iterator](); - let iter2 = other[Symbol.iterator](); - - while (true) { - const x1 = iter1.next(); - const x2 = iter2.next(); - - if (x1.done || x2.done) { - break; - } - - yield [x1.value, x2.value] as [T, U]; - } - } - - return Range_fromIterable(res()); - } - - skip(n: number) { - const iterable = this.iterable; - - function* res() { - let iter = iterable[Symbol.iterator](); - - for (let i = 0; i < n; i++) { - iter.next(); - } - - while (true) { - const x = iter.next(); - - if (x.done) { - break; - } - - yield x.value; - } - } - - return Range_fromIterable(res()); - } - - reduce(state: S, fn: (state: S, x: T) => S) { - for (const x of this.iterable) { - state = fn(state, x); - } - - return state; - } -} - -function hasKey( - obj: unknown, - key: K, -): obj is Obj & Record { - return (obj as Record)[key] !== undefined; -} - -function never(x: never): never { - throw new Error(`Unexpected value: ${x}`); -}