|
| 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 |
0 commit comments