diff options
Diffstat (limited to 'lib/types/collections/cons.ts')
-rw-r--r-- | lib/types/collections/cons.ts | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/lib/types/collections/cons.ts b/lib/types/collections/cons.ts new file mode 100644 index 0000000..b671d71 --- /dev/null +++ b/lib/types/collections/cons.ts @@ -0,0 +1,108 @@ +import { IOptional, Mapper, Optional, Supplier } from '@emprespresso/pengueno'; + +export interface ICons<T> extends Iterable<T> { + readonly value: T; + readonly next: IOptional<ICons<T>>; + + readonly replace: Mapper<T, ICons<T>>; + readonly before: Mapper<IOptional<ICons<T>>, ICons<T>>; +} + +export class Cons<T> implements ICons<T> { + constructor( + public readonly value: T, + public readonly next: IOptional<ICons<T>> = Optional.none(), + ) {} + + public before(head: IOptional<ICons<T>>): ICons<T> { + return new Cons<T>(this.value, head); + } + + public replace(_value: T): ICons<T> { + return new Cons<T>(_value, this.next); + } + + *[Symbol.iterator]() { + for (let cur = Optional.some<ICons<T>>(this); cur.present(); cur = cur.flatMap((cur) => cur.next)) { + yield cur.get().value; + } + } + + static addOnto<T>(items: Iterable<T>, tail: IOptional<ICons<T>>): IOptional<ICons<T>> { + return Array.from(items) + .reverse() + .reduce((cons, value) => Optional.from<ICons<T>>(new Cons<T>(value, cons)), tail); + } + + static from<T>(items: Iterable<T>): IOptional<ICons<T>> { + return Cons.addOnto(items, Optional.none()); + } +} + +export interface IZipper<T> extends Iterable<T> { + readonly read: Supplier<IOptional<T>>; + readonly next: Supplier<IOptional<IZipper<T>>>; + readonly previous: Supplier<IOptional<IZipper<T>>>; + + readonly prependChunk: Mapper<Iterable<T>, IZipper<T>>; + readonly prepend: Mapper<T, IZipper<T>>; + readonly remove: Supplier<IZipper<T>>; + readonly replace: Mapper<T, IZipper<T>>; +} + +export class ListZipper<T> implements IZipper<T> { + private constructor( + private readonly reversedPathToHead: IOptional<ICons<T>>, + private readonly currentHead: IOptional<ICons<T>>, + ) {} + + public read(): IOptional<T> { + return this.currentHead.map(({ value }) => value); + } + + public next(): IOptional<IZipper<T>> { + return this.currentHead.map<IZipper<T>>( + (head) => new ListZipper<T>(Optional.some(head.before(this.reversedPathToHead)), head.next), + ); + } + + public previous(): IOptional<IZipper<T>> { + return this.reversedPathToHead.map<IZipper<T>>( + (lastVisited) => new ListZipper<T>(lastVisited.next, Optional.some(lastVisited.before(this.currentHead))), + ); + } + + public prependChunk(values: Iterable<T>): IZipper<T> { + return new ListZipper<T>(Cons.addOnto(Array.from(values).reverse(), this.reversedPathToHead), this.currentHead); + } + + public prepend(value: T): IZipper<T> { + return this.prependChunk([value]); + } + + public remove(): IZipper<T> { + const newHead = this.currentHead.flatMap((right) => right.next); + return new ListZipper<T>(this.reversedPathToHead, newHead); + } + + public replace(value: T): IZipper<T> { + const newHead = this.currentHead.map((right) => right.replace(value)); + return new ListZipper<T>(this.reversedPathToHead, newHead); + } + + *[Symbol.iterator]() { + let head: ListZipper<T> = this; + for (let prev = head.previous(); prev.present(); prev = prev.flatMap((p) => p.previous())) { + head = <ListZipper<T>>prev.get(); + } + if (head.currentHead.present()) yield* head.currentHead.get(); + } + + public collection() { + return Array.from(this); + } + + static from<T>(iterable: Iterable<T>): ListZipper<T> { + return new ListZipper(Optional.none(), Cons.from(iterable)); + } +} |