main
ts 111 lines 2.39 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 // An immutable stack data structure supporting O(1) push/pop operations.
9 export type Stack<T> = Node<T> | Empty<T>;
10
11 // Static assertion that Stack<T> is a StackInterface<T>
12 function _assertStackInterface<T>(stack: Stack<T>): void {
13 let _: StackInterface<T> = stack;
14 }
15
16 /*
17 * Internal interface to enforce consistent behavior btw Node/Empty variants
18 * Note that we export a union rather than the interface so that it is impossible
19 * to create additional variants: a Stack should always be exactly a Node or Empty
20 * instance.
21 */
22 interface StackInterface<T> {
23 push(value: T): StackInterface<T>;
24
25 pop(): StackInterface<T>;
26
27 contains(value: T): boolean;
28 find(fn: (value: T) => boolean): boolean;
29
30 each(fn: (value: T) => void): void;
31
32 get value(): T | null;
33
34 print(fn: (node: T) => string): string;
35 }
36
37 export function create<T>(value: T): Stack<T> {
38 return new Node(value);
39 }
40
41 export function empty<T>(): Stack<T> {
42 return EMPTY as any;
43 }
44
45 class Node<T> implements StackInterface<T> {
46 #value: T;
47 #next: Stack<T>;
48
49 constructor(value: T, next: Stack<T> = EMPTY as any) {
50 this.#value = value;
51 this.#next = next;
52 }
53
54 push(value: T): Node<T> {
55 return new Node(value, this);
56 }
57
58 pop(): Stack<T> {
59 return this.#next;
60 }
61
62 find(fn: (value: T) => boolean): boolean {
63 return fn(this.#value) ? true : this.#next.find(fn);
64 }
65
66 contains(value: T): boolean {
67 return (
68 value === this.#value ||
69 (this.#next !== null && this.#next.contains(value))
70 );
71 }
72 each(fn: (value: T) => void): void {
73 fn(this.#value);
74 this.#next.each(fn);
75 }
76
77 get value(): T {
78 return this.#value;
79 }
80
81 print(fn: (node: T) => string): string {
82 return fn(this.#value) + this.#next.print(fn);
83 }
84 }
85
86 class Empty<T> implements StackInterface<T> {
87 push(value: T): Stack<T> {
88 return new Node(value as T, this as Stack<T>);
89 }
90 pop(): Stack<T> {
91 return this;
92 }
93
94 find(_fn: (value: T) => boolean): boolean {
95 return false;
96 }
97 contains(_value: T): boolean {
98 return false;
99 }
100 each(_fn: (value: T) => void): void {
101 return;
102 }
103 get value(): T | null {
104 return null;
105 }
106 print(_: (node: T) => string): string {
107 return '';
108 }
109 }
110
111 const EMPTY: Stack<void> = new Empty();