1module Dict (empty,singleton,insert,update
2 ,get,getOrElse,getOrFail
12{-| A dictionary mapping unique keys to values. The keys can be any comparable
13type. This includes `Int`, `Float`, `Time`, `Char`, `String`, and tuples or
14lists of comparable types.
16Insert, remove, and query operations all take *O(log n)* time.
19@docs empty, singleton, insert, update, remove
22@docs member, get, getOrElse, getOrFail
25@docs union, intersect, diff
28@docs keys, values, toList, fromList
31@docs map, foldl, foldr, filter, partition
43-- BBlack and NBlack should only be used during the deletion
44-- algorithm. Any other occurrence is a bug and should fail an assert.
47 -- ^ Double Black, counts as 2 blacks for the invariant
49 -- ^ Negative Black, counts as -1 blacks for the invariant
53showNColor : NColor -> String
54showNColor c = case c of
60data LeafColor = LBlack
61 -- ^ Double Black, counts as 2
64showLColor : LeafColor -> String
65showLColor c = case c of
69data Dict k v = RBNode NColor k v (Dict k v) (Dict k v)
72{-| Create an empty dictionary. -}
73empty : Dict comparable v
76min : Dict k v -> (k,v)
79 RBNode _ k v (RBEmpty LBlack) _ -> (k,v)
80 RBNode _ _ _ l _ -> min l
81 RBEmpty LBlack -> Native.Error.raise "(min Empty) is not defined"
83max : Dict k v -> (k, v)
86 RBNode _ k v _ (RBEmpty _) -> (k,v)
87 RBNode _ _ _ _ r -> max r
88 RBEmpty _ -> Native.Error.raise "(max Empty) is not defined"
90{-| Get the value associated with a key. If the key is not found, return
91`Nothing`. This is useful when you are not sure if a key will be in the
94 animals = fromList [ ("Tom", Cat), ("Jerry", Mouse) ]
96 get "Tom" animals == Just Cat
97 get "Mouse" animals == Just Mouse
98 get "Spike" animals == Nothing
100The `getOrElse` and `getOrFail` are built-in ways to handle common ways of
101using the resulting `Maybe`.
103get : comparable -> Dict comparable v -> Maybe v
106 RBEmpty LBlack -> Nothing
108 case Native.Utils.compare k k' of
113{-| Get the value associated with a key. If the key is not found,
114return a default value.
116 animals = fromList [ ("Tom", Cat), ("Jerry", Mouse) ]
118 getOrElse Dog "Tom" animals == Cat
119 getOrElse Dog "Mouse" animals == Mouse
120 getOrElse Dog "Spike" animals == Dog
122getOrElse : v -> comparable -> Dict comparable v -> v
125 RBEmpty LBlack -> base
127 case Native.Utils.compare k k' of
128 LT -> getOrElse base k l
130 GT -> getOrElse base k r
132{-| Get the value associated with a key.
134 animals = fromList [ ("Tom", Cat), ("Jerry", Mouse) ]
136 getOrFail "Tom" animals == Cat
137 getOrFail "Mouse" animals == Mouse
138 getOrFail "Spike" animals == -- Runtime Error!
140Warning: this function will result in a runtime error if the key is not found,
141so it is best to use `get` or `getOrElse` unless you are sure the key will be
144getOrFail : comparable -> Dict comparable v -> v
147 RBEmpty LBlack -> Native.Error.raise "key not found when using 'getOrFail'"
149 case Native.Utils.compare k k' of
154{-| Determine if a key is in a dictionary. -}
155member : comparable -> Dict comparable v -> Bool
157member k t = isJust <| get k t
159ensureBlackRoot : Dict k v -> Dict k v
162 RBNode Red k v l r -> RBNode Black k v l r
163 RBNode Black _ _ _ _ -> t
166{-| Insert a key-value pair into a dictionary. Replaces value when there is
168insert : comparable -> v -> Dict comparable v -> Dict comparable v
169insert k v t = let u _ = Just v in
172{-| Remove a key-value pair from a dictionary. If the key is not found,
173no changes are made. -}
174remove : comparable -> Dict comparable v -> Dict comparable v
175remove k t = let u _ = Nothing in
178data Flag = Insert | Remove | Same
179showFlag : Flag -> String
180showFlag f = case f of
185{-| Update the value of a dictionary for a specific key with a given function. -}
186update : comparable -> (Maybe v -> Maybe v) -> Dict comparable v -> Dict comparable v
189 RBEmpty LBlack -> case u Nothing of
190 Nothing -> (Same, empty)
191 Just v -> (Insert, RBNode Red k v empty empty)
192 RBNode c k' v l r -> case Native.Utils.compare k k' of
193 EQ -> case u (Just v) of
194 Nothing -> (Remove, rem c l r)
195 Just v' -> (Same, RBNode c k' v' l r)
196 LT -> let (fl, l') = up l in
198 Same -> (Same, RBNode c k' v l' r)
199 Insert -> (Insert, balance c k' v l' r)
200 Remove -> (Remove, bubble c k' v l' r)
201 GT -> let (fl, r') = up r in
203 Same -> (Same, RBNode c k' v l r')
204 Insert -> (Insert, balance c k' v l r')
205 Remove -> (Remove, bubble c k' v l r')
209 Insert -> ensureBlackRoot t'
212{-| Create a dictionary with one key-value pair. -}
213singleton : comparable -> v -> Dict comparable v
214singleton k v = insert k v (RBEmpty LBlack)
216isBBlack : Dict k v -> Bool
217isBBlack t = case t of
218 RBNode BBlack _ _ _ _ -> True
219 RBEmpty LBBlack -> True
222moreBlack : NColor -> NColor
223moreBlack c = case c of
227 BBlack -> Native.Error.raise "Can't make a double black node more black!"
229lessBlack : NColor -> NColor
230lessBlack c = case c of
234 NBlack -> Native.Error.raise "Can't make a negative black node less black!"
236lessBlackTree : Dict k v -> Dict k v
237lessBlackTree t = case t of
238 RBNode c k v l r -> RBNode (lessBlack c) k v l r
239 RBEmpty LBBlack -> RBEmpty LBlack
241reportRemBug : String -> NColor -> String -> String -> a
242reportRemBug msg c lgot rgot =
243 Native.Error.raise <| List.concat [
244 "Internal red-black tree invariant violated, expected ",
252 "\nPlease report this bug to https://github.com/elm-lang/Elm/issues"
255-- Remove the top node from the tree, may leave behind BBlacks
256rem : NColor -> Dict k v -> Dict k v -> Dict k v
257rem c l r = case (l, r) of
258 ((RBEmpty _), (RBEmpty _)) -> case c of
259 Red -> RBEmpty LBlack
260 Black -> RBEmpty LBBlack
261 ((RBEmpty cl), (RBNode cr k' v' l' r')) ->
263 (Black, LBlack, Red) -> RBNode Black k' v' l' r'
264 _ -> reportRemBug "Black, LBlack, Red" c (showLColor cl) (showNColor cr)
265 ((RBNode cl k' v' l' r'), (RBEmpty cr)) ->
267 (Black, Red, LBlack) -> RBNode Black k' v' l' r'
268 _ -> reportRemBug "Black, Red, LBlack" c (showNColor cl) (showLColor cr)
269 -- l and r are both RBNodes
270 ((RBNode cl kl vl ll rl), (RBNode cr kr vr lr rr)) ->
271 let l = RBNode cl kl vl ll rl
272 r = RBNode cr kr vr lr rr
274 l' = remove_max cl kl vl ll rl
277-- Kills a BBlack or moves it upward, may leave behind NBlack
278bubble : NColor -> k -> v -> Dict k v -> Dict k v -> Dict k v
279bubble c k v l r = if isBBlack l || isBBlack r
280 then balance (moreBlack c) k v (lessBlackTree l) (lessBlackTree r)
281 else RBNode c k v l r
283-- Removes rightmost node, may leave root as BBlack
284remove_max : NColor -> k -> v -> Dict k v -> Dict k v -> Dict k v
285remove_max c k v l r = case r of
286 RBEmpty _ -> rem c l r
287 RBNode cr kr vr lr rr
288 -> bubble c k v l (remove_max cr kr vr lr rr)
290-- generalized tree balancing act
291balance : NColor -> k -> v -> Dict k v -> Dict k v -> Dict k v
294 balance_node (RBNode c k v l r)
296blackish : Dict k v -> Bool
297blackish t = case t of
298 RBNode c _ _ _ _ -> c == Black || c == BBlack
301balance_node : Dict k v -> Dict k v
303 let assemble col xk xv yk yv zk zv a b c d =
304 RBNode (lessBlack col) yk yv (RBNode Black xk xv a b) (RBNode Black zk zv c d)
308 RBNode col zk zv (RBNode Red yk yv (RBNode Red xk xv a b) c) d ->
309 assemble col xk xv yk yv zk zv a b c d
310 RBNode col zk zv (RBNode Red xk xv a (RBNode Red yk yv b c)) d ->
311 assemble col xk xv yk yv zk zv a b c d
312 RBNode col xk xv a (RBNode Red zk zv (RBNode Red yk yv b c) d) ->
313 assemble col xk xv yk yv zk zv a b c d
314 RBNode col xk xv a (RBNode Red yk yv b (RBNode Red zk zv c d)) ->
315 assemble col xk xv yk yv zk zv a b c d
317 RBNode BBlack xk xv a (RBNode NBlack zk zv (RBNode Black yk yv b c) d) ->
319 (RBNode Black _ _ _ _) ->
320 RBNode Black yk yv (RBNode Black xk xv a b) (balance Black zk zv c (redden d))
323 RBNode BBlack zk zv (RBNode NBlack xk xv a (RBNode Black yk yv b c)) d ->
325 (RBNode Black _ _ _ _) ->
326 RBNode Black yk yv (balance Black xk xv (redden a) b) (RBNode Black zk zv c d)
331-- make the top node black
332blacken : Dict k v -> Dict k v
334 RBEmpty _ -> RBEmpty LBlack
335 RBNode _ k v l r -> RBNode Black k v l r
337-- make the top node red
338redden : Dict k v -> Dict k v
340 RBEmpty _ -> Native.Error.raise "can't make a Leaf red"
341 RBNode _ k v l r -> RBNode Red k v l r
343{-| Apply a function to all values in a dictionary. -}
344map : (a -> b) -> Dict comparable a -> Dict comparable b
347 RBEmpty LBlack -> RBEmpty LBlack
348 RBNode c k v l r -> RBNode c k (f v) (map f l) (map f r)
350{-| Fold over the key-value pairs in a dictionary, in order from lowest
351key to highest key. -}
352foldl : (comparable -> v -> b -> b) -> b -> Dict comparable v -> b
355 RBEmpty LBlack -> acc
356 RBNode _ k v l r -> foldl f (f k v (foldl f acc l)) r
358{-| Fold over the key-value pairs in a dictionary, in order from highest
360foldr : (comparable -> v -> b -> b) -> b -> Dict comparable v -> b
363 RBEmpty LBlack -> acc
364 RBNode _ k v l r -> foldr f (f k v (foldr f acc r)) l
366{-| Combine two dictionaries. If there is a collision, preference is given
367to the first dictionary. -}
368union : Dict comparable v -> Dict comparable v -> Dict comparable v
369union t1 t2 = foldl insert t2 t1
371{-| Keep a key-value pair when its key appears in the second dictionary.
372Preference is given to values in the first dictionary. -}
373intersect : Dict comparable v -> Dict comparable v -> Dict comparable v
374intersect t1 t2 = filter (\k _ -> k `member` t2) t1
376{-| Keep a key-value pair when its key does not appear in the second dictionary.
377Preference is given to the first dictionary. -}
378diff : Dict comparable v -> Dict comparable v -> Dict comparable v
379diff t1 t2 = foldl (\k v t -> remove k t) t1 t2
381{-| Get all of the keys in a dictionary. -}
382keys : Dict comparable v -> [comparable]
383keys t = foldr (\k v acc -> k :: acc) [] t
385{-| Get all of the values in a dictionary. -}
386values : Dict comparable v -> [v]
387values t = foldr (\k v acc -> v :: acc) [] t
389{-| Convert a dictionary into an association list of key-value pairs. -}
390toList : Dict comparable v -> [(comparable,v)]
391toList t = foldr (\k v acc -> (k,v) :: acc) [] t
393{-| Convert an association list into a dictionary. -}
394fromList : [(comparable,v)] -> Dict comparable v
395fromList assocs = List.foldl (\(k,v) d -> insert k v d) empty assocs
397{-| Keep a key-value pair when it satisfies a predicate. -}
398filter : (comparable -> v -> Bool) -> Dict comparable v -> Dict comparable v
400 let add k v t = if p k v then insert k v t else t
401 in foldl add empty dict
403{-| Partition a dictionary according to a predicate. The first dictionary
404contains all key-value pairs which satisfy the predicate, and the second
407partition : (comparable -> v -> Bool) -> Dict comparable v -> (Dict comparable v, Dict comparable v)
409 let add k v (t1, t2) = if p k v
410 then (insert k v t1, t2)
411 else (t1, insert k v t2)
412 in foldl add (empty, empty) dict
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list