summaryrefslogtreecommitdiff
path: root/lib/types/collections/cons.ts
blob: b671d715ffa809f7f552be9424d0f4c74e74a386 (plain)
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));
    }
}