From 9970036d203ba2d0a46b35ba6fad21d49441cdd4 Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sun, 27 Jul 2025 17:03:10 -0700 Subject: hai --- lib/types/collections/cons.ts | 108 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 lib/types/collections/cons.ts (limited to 'lib/types/collections/cons.ts') 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 extends Iterable { + readonly value: T; + readonly next: IOptional>; + + readonly replace: Mapper>; + readonly before: Mapper>, ICons>; +} + +export class Cons implements ICons { + constructor( + public readonly value: T, + public readonly next: IOptional> = Optional.none(), + ) {} + + public before(head: IOptional>): ICons { + return new Cons(this.value, head); + } + + public replace(_value: T): ICons { + return new Cons(_value, this.next); + } + + *[Symbol.iterator]() { + for (let cur = Optional.some>(this); cur.present(); cur = cur.flatMap((cur) => cur.next)) { + yield cur.get().value; + } + } + + static addOnto(items: Iterable, tail: IOptional>): IOptional> { + return Array.from(items) + .reverse() + .reduce((cons, value) => Optional.from>(new Cons(value, cons)), tail); + } + + static from(items: Iterable): IOptional> { + return Cons.addOnto(items, Optional.none()); + } +} + +export interface IZipper extends Iterable { + readonly read: Supplier>; + readonly next: Supplier>>; + readonly previous: Supplier>>; + + readonly prependChunk: Mapper, IZipper>; + readonly prepend: Mapper>; + readonly remove: Supplier>; + readonly replace: Mapper>; +} + +export class ListZipper implements IZipper { + private constructor( + private readonly reversedPathToHead: IOptional>, + private readonly currentHead: IOptional>, + ) {} + + public read(): IOptional { + return this.currentHead.map(({ value }) => value); + } + + public next(): IOptional> { + return this.currentHead.map>( + (head) => new ListZipper(Optional.some(head.before(this.reversedPathToHead)), head.next), + ); + } + + public previous(): IOptional> { + return this.reversedPathToHead.map>( + (lastVisited) => new ListZipper(lastVisited.next, Optional.some(lastVisited.before(this.currentHead))), + ); + } + + public prependChunk(values: Iterable): IZipper { + return new ListZipper(Cons.addOnto(Array.from(values).reverse(), this.reversedPathToHead), this.currentHead); + } + + public prepend(value: T): IZipper { + return this.prependChunk([value]); + } + + public remove(): IZipper { + const newHead = this.currentHead.flatMap((right) => right.next); + return new ListZipper(this.reversedPathToHead, newHead); + } + + public replace(value: T): IZipper { + const newHead = this.currentHead.map((right) => right.replace(value)); + return new ListZipper(this.reversedPathToHead, newHead); + } + + *[Symbol.iterator]() { + let head: ListZipper = this; + for (let prev = head.previous(); prev.present(); prev = prev.flatMap((p) => p.previous())) { + head = >prev.get(); + } + if (head.currentHead.present()) yield* head.currentHead.get(); + } + + public collection() { + return Array.from(this); + } + + static from(iterable: Iterable): ListZipper { + return new ListZipper(Optional.none(), Cons.from(iterable)); + } +} -- cgit v1.2.3-70-g09d2