1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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));
}
}
|