Skip to content

Commit b30114c

Browse files
committed
Add Map module (rewrite of 'bst.trp')
1 parent a4a341c commit b30114c

File tree

5 files changed

+690
-56
lines changed

5 files changed

+690
-56
lines changed

lib/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ build:
99
$(COMPILER) ./String.trp -l
1010
$(COMPILER) ./Hash.trp -l
1111
$(COMPILER) ./Unit.trp -l
12+
$(COMPILER) ./Map.trp -l
1213
$(COMPILER) ./StencilVector.trp -l
1314
$(COMPILER) ./HashMap.trp -l
1415
$(COMPILER) ./HashSet.trp -l
@@ -21,7 +22,6 @@ build:
2122
$(COMPILER) ./stdio.trp -l
2223
$(COMPILER) ./raft.trp -l
2324
$(COMPILER) ./raft_debug.trp -l
24-
$(COMPILER) ./bst.trp -l
2525
$(COMPILER) ./localregistry.trp -l
2626

2727
clean:

lib/Map.trp

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import Number
2+
import List
3+
4+
let (* TODO: The map is implemented as an unbalanced binary search tree. This opens up the
5+
* possibility for an adversary to induce a denial of service by choosing a bad
6+
* order of insertion.
7+
*)
8+
9+
(*--- Construction ---*)
10+
11+
(** Returns an empty map using the comparator, `cmp`. *)
12+
fun empty cmp = (cmp, ())
13+
14+
(** Returns a map using the comparator function, `cmp` with the single `key`-`value` pair. *)
15+
fun singleton cmp key value = (cmp, ((), (key , value), ()))
16+
17+
(** Returns the map with the given `key`-`value` pair inserted. *)
18+
fun insert (cmp,t) key value =
19+
let fun insertRec () = ((), (key, value), ())
20+
| insertRec (l, (k,v), r) =
21+
if key = k
22+
then (l, (k, value), r)
23+
else if cmp key k then (insertRec l, (k,v), r) else (l, (k,v), insertRec r)
24+
in (cmp, insertRec t) end
25+
26+
(*--- Queries ---*)
27+
28+
(** Returns `true` if the map is empty. *)
29+
fun null (_, ()) = true
30+
| null _ = false
31+
32+
(** Returns the `[value]` associated with the `key` if it exists; otherwise, `[]`. *)
33+
(* HACK (#53): Use option type instead of list. *)
34+
fun findOpt (cmp,t) key =
35+
let fun findRec () = []
36+
| findRec (l, (k,v), r) = if key = k then [v]
37+
else if cmp key k then findRec l else findRec r
38+
in findRec t end
39+
40+
(** Returns the value associated with the `key`. *)
41+
fun find m key = case findOpt m key of [v] => v
42+
43+
(** Returns `true` if the given key is associated with a value. *)
44+
fun mem m key = case findOpt m key of [] => false
45+
| _ => true
46+
47+
(** Number of key-value pairs stored in the given map. *)
48+
(* TODO (Optimisation): Store size at root to make this O(1)? *)
49+
fun size (_,t) =
50+
let fun sizeRec () = 0
51+
| sizeRec (l, (_,_), r) = (sizeRec l) + 1 + (sizeRec r)
52+
in sizeRec t end
53+
54+
(** Depth of the tree. *)
55+
fun depth (_,t) =
56+
let fun depthRec () = 0
57+
| depthRec (l, (_,_), r) = 1 + Number.max ((depthRec l), (depthRec r))
58+
in depthRec t end
59+
60+
(*--- Priority Queue ---*)
61+
62+
(** The smallest `[(key,value)]` if it exists; otherwise, `[]`. *)
63+
fun minOpt (_,t) =
64+
let fun minRec () = []
65+
| minRec ((), (k,v), _) = [(k,v)]
66+
| minRec (l, (_,_), _) = minRec l
67+
in minRec t
68+
end
69+
70+
(** The smallest `(key,value)`. *)
71+
fun min m = case minOpt m of [kv] => kv
72+
73+
(** The smallest `[(key,value)]` (if any) together with the tree updated to not include said
74+
* value. If the tree is empty, then `[]` is returned. *)
75+
fun extractMinOpt (cmp, t) =
76+
let fun extractMinRec () = ([], ())
77+
| extractMinRec ((), (k,v), r) = ([(k,v)], r)
78+
| extractMinRec (l, (k,v), r) = let val (kv', l') = extractMinRec l
79+
in (kv', (l', (k,v), r)) end
80+
81+
val (kv', t') = extractMinRec t
82+
in (kv', (cmp, t')) end
83+
84+
(** The smallest `(key,value)` together with the tree updated to not include said value. *)
85+
fun extractMin m = case extractMinOpt m of ([kv], m') => (kv, m')
86+
87+
(*--- Destruction ---*)
88+
89+
(** Returns the map without the given `key` and its value removed. *)
90+
fun remove (cmp,t) key =
91+
let fun removeRec () = ()
92+
| removeRec (l, (k,v), ()) = if key = k then l else (l, (k,v), ())
93+
| removeRec ((), (k,v), r) = if key = k then r else ((), (k,v), r)
94+
| removeRec (l, (k,v), r) =
95+
if key = k
96+
then let val ((k',v'), (_,r')) = extractMin (cmp, r)
97+
in (l, (k',v'), r') end
98+
else if cmp key k then (removeRec l, (k,v), r) else (l, (k,v), removeRec r)
99+
in (cmp, removeRec t) end
100+
101+
(*--- Manipulation ---*)
102+
103+
(** Fold function `f` over all key-value pairs in the map in order of the keys.
104+
* Not tail-recursive. *)
105+
fun foldl f y (_,t) =
106+
let fun foldlRec y' () = y'
107+
| foldlRec y' (l, (k,v), r) = foldlRec (f ((k,v), foldlRec y' l)) r
108+
in foldlRec y t end
109+
110+
(** Fold function `f` over all key-value pairs in the map in reverse order of the keys.
111+
* Not tail-recursive. *)
112+
fun foldr f y (_,t) =
113+
let fun foldrRec y' () = y'
114+
| foldrRec y' (l, (k,v), r) = foldrRec (f ((k,v), foldrRec y' r)) l
115+
in foldrRec y t end
116+
117+
(** Map function `f` onto all key-value pairs in the map. The new map uses the comparator `cmp`.
118+
* Not tail-recursive. *)
119+
fun map cmp f m =
120+
let fun f' (kv, m') = let val (k', v') = f kv
121+
in insert m' k' v' end
122+
in foldl f' (empty cmp) m end
123+
124+
(** Filters key-value pairs in the given map. Not tail-recursive. *)
125+
fun filter f (cmp, t) =
126+
let fun filterRec () = ()
127+
| filterRec (l, kv, ()) = if f kv then (filterRec l, kv, ()) else filterRec l
128+
| filterRec ((), kv, r ) = if f kv then ((), kv, filterRec r) else filterRec r
129+
| filterRec (l, kv, r ) =
130+
if f kv
131+
then (filterRec l, kv, filterRec r)
132+
else case filterRec r of () => filterRec l
133+
| r' => let val (kv', (_, r'')) = extractMin (cmp, r')
134+
in (filterRec l, kv', r'') end
135+
in (cmp, filterRec t) end
136+
137+
(* TODO: merge / union, split. *)
138+
139+
(*--- Comparison ---*)
140+
141+
(* TODO: `foldl` using `foldl` *)
142+
143+
(*--- List Conversion ---*)
144+
145+
(** A list of all keys stored in the given map in ascending order. *)
146+
fun keys m = foldr (fn ((k,_), acc) => k::acc) [] m
147+
148+
(** A list of all keys stored in the given map. *)
149+
fun values m = foldr (fn ((_,v), acc) => v::acc) [] m
150+
151+
(** A list of key-value pairs stored in the given map. *)
152+
fun toDescList m = foldl (fn ((k,v), acc) => (k,v)::acc) [] m
153+
154+
(** A list of key-value pairs stored in the given map. *)
155+
fun toAscList m = foldr (fn ((k,v), acc) => (k,v)::acc) [] m
156+
157+
(** Alias for `toAscList`. *)
158+
val toList = toAscList
159+
160+
(** The map (using the provided comparator function) that contains all key-value pairs in the
161+
* given list. Tail-recursive. *)
162+
fun fromList cmp xs = List.foldl (fn ((k,v), m) => insert m k v) (empty cmp) xs
163+
164+
(*--- Module ---*)
165+
val Map = {
166+
empty,
167+
singleton,
168+
insert,
169+
null,
170+
size,
171+
depth,
172+
findOpt,
173+
find,
174+
mem,
175+
minOpt,
176+
min,
177+
extractMinOpt,
178+
extractMin,
179+
remove,
180+
foldl,
181+
foldr,
182+
map,
183+
filter,
184+
keys,
185+
values,
186+
toDescList,
187+
toAscList,
188+
toList,
189+
fromList
190+
}
191+
192+
in [ ("Map", Map) ]
193+
end

lib/bst.trp

Lines changed: 0 additions & 55 deletions
This file was deleted.

0 commit comments

Comments
 (0)