elm/libraries/Dict.elm

403 lines
12 KiB
Elm
Raw Normal View History

module Dict (empty,singleton,insert
2012-10-21 11:52:46 +00:00
,lookup,findWithDefault
,remove,member
,foldl,foldr,map
,union,intersect,diff
,keys,values
,toList,fromList
) where
import Maybe as Maybe
import Native.Error as Error
import List as List
import Native.Utils (compare)
2012-10-10 21:37:42 +00:00
data NColor = Red | Black
data Dict k v = RBNode NColor k v (Dict k v) (Dict k v) | RBEmpty
2012-10-10 21:37:42 +00:00
-- Create an empty dictionary.
empty : Dict (Comparable k) v
empty = RBEmpty
2012-10-10 21:37:42 +00:00
{-- Helpers for checking invariants
2012-10-10 21:37:42 +00:00
-- Check that the tree has an equal number of black nodes on each path
equal_pathLen t =
2012-10-10 21:37:42 +00:00
let path_numBlacks t =
case t of
RBEmpty -> 1
RBNode col _ _ l r ->
2012-10-10 21:37:42 +00:00
let { bl = path_numBlacks l ; br = path_numBlacks r } in
if bl /= br || bl == 0-1 || br == 0-1
then 0-1
else bl + (if col == Red then 0 else 1)
in 0-1 /= path_numBlacks t
rootBlack t =
2012-10-10 21:37:42 +00:00
case t of
RBEmpty -> True
RBNode Black _ _ _ _ -> True
_ -> False
2012-10-10 21:37:42 +00:00
redBlack_children t =
case t of
{ RBNode Red _ _ (RBNode Red _ _ _ _) _ -> False
; RBNode Red _ _ _ (RBNode Red _ _ _ _) -> False
; RBEmpty -> True
; RBNode _ _ _ l r -> redBlack_children l && redBlack_children r
2012-10-10 21:37:42 +00:00
}
findExtreme f t =
case t of
{ RBEmpty -> Nothing
; RBNode c k _ l r ->
2012-10-10 21:37:42 +00:00
case findExtreme f (f (l,r)) of
{ Nothing -> Just k
; Just k' -> Just k' }
}
2012-10-10 21:37:42 +00:00
findminRbt t = findExtreme fst t
findmaxRbt t = findExtreme snd t
-- "Option LT than"
-- Returns True if either xo or yo is Nothing
-- Otherwise returns the result of comparing the values using f
optionRelation f u xo yo =
case (xo,yo) of
{ (Nothing,_) -> u
; (_,Nothing) -> u
; (Just x, Just y) -> f x y }
olt xo yo = optionRelation (< ) True xo yo
olte xo yo = optionRelation (<=) True xo yo
ordered t =
case t of
{ RBEmpty -> True
; RBNode c k v l r ->
2012-10-10 21:37:42 +00:00
let (lmax,rmin) = (findmaxRbt l, findminRbt r) in
olte lmax (Just k) && olte (Just k) rmin && ordered l && ordered r
}
-- Check that there aren't any right red nodes in the tree *)
leftLeaning t =
2012-10-10 21:37:42 +00:00
case t of
{ RBEmpty -> True
; RBNode _ _ _ (RBNode Black _ _ _ _) (RBNode Red _ _ _ _) -> False
; RBNode _ _ _ RBEmpty (RBNode Red _ _ _ _) -> False
; RBNode _ _ _ l r -> (leftLeaning l) && (leftLeaning r)
2012-10-10 21:37:42 +00:00
}
invariants_hold t =
ordered t && rootBlack t && redBlack_children t &&
2012-10-10 21:37:42 +00:00
equal_pathLen t && leftLeaning t
--** End invariant helpers *****
--}
2012-10-10 21:37:42 +00:00
min : Dict k v -> (k,v)
2012-10-10 21:37:42 +00:00
min t =
case t of
RBNode _ k v RBEmpty _ -> (k,v)
RBNode _ _ _ l _ -> min l
RBEmpty -> Error.raise "(min Empty) is not defined"
2012-10-10 21:37:42 +00:00
{--
2012-10-10 21:37:42 +00:00
max t =
case t of
{ RBNode _ k v _ RBEmpty -> (k,v)
; RBNode _ _ _ _ r -> max r
; RBEmpty -> Error.raise "(max Empty) is not defined"
2012-10-10 21:37:42 +00:00
}
--}
2012-10-10 21:37:42 +00:00
-- Lookup the value associated with a key.
lookup : Comparable k -> Dict (Comparable k) v -> Maybe v
2012-10-10 21:37:42 +00:00
lookup k t =
case t of
2013-05-06 09:30:50 +00:00
RBEmpty -> Maybe.Nothing
RBNode _ k' v l r ->
2012-10-10 21:37:42 +00:00
case compare k k' of
LT -> lookup k l
2013-05-06 09:30:50 +00:00
EQ -> Maybe.Just v
GT -> lookup k r
2012-10-10 21:37:42 +00:00
-- Find the value associated with a key. If the key is not found,
-- return the default value.
findWithDefault : v -> Comparable k -> Dict (Comparable k) v -> v
findWithDefault base k t =
case t of
RBEmpty -> base
RBNode _ k' v l r ->
case compare k k' of
LT -> findWithDefault base k l
EQ -> v
GT -> findWithDefault base k r
2012-10-21 11:52:46 +00:00
{--
-- Find the value associated with a key. If the key is not found, there will be a runtime error.
find k t =
case t of
{ RBEmpty -> Error.raise "Key was not found in dictionary!"
; RBNode _ k' v l r ->
case compare k k' of
{ LT -> find k l
; EQ -> v
; GT -> find k r }
}
2012-10-21 11:52:46 +00:00
--}
-- Determine if a key is in a dictionary.
member : Comparable k -> Dict (Comparable k) v -> Bool
-- Does t contain k?
member k t = Maybe.isJust $ lookup k t
2012-10-10 21:37:42 +00:00
rotateLeft : Dict k v -> Dict k v
2012-10-10 21:37:42 +00:00
rotateLeft t =
case t of
RBNode cy ky vy a (RBNode cz kz vz b c) -> RBNode cy kz vz (RBNode Red ky vy a b) c
_ -> Error.raise "rotateLeft of a node without enough children"
2012-10-10 21:37:42 +00:00
-- rotateRight -- the reverse, and
2012-10-10 21:37:42 +00:00
-- makes Y have Z's color, and makes Z Red.
rotateRight : Dict k v -> Dict k v
2012-10-10 21:37:42 +00:00
rotateRight t =
case t of
RBNode cz kz vz (RBNode cy ky vy a b) c -> RBNode cz ky vy a (RBNode Red kz vz b c)
_ -> Error.raise "rotateRight of a node without enough children"
2012-10-10 21:37:42 +00:00
rotateLeftIfNeeded : Dict k v -> Dict k v
2012-10-10 21:37:42 +00:00
rotateLeftIfNeeded t =
case t of
RBNode _ _ _ _ (RBNode Red _ _ _ _) -> rotateLeft t
_ -> t
2012-10-10 21:37:42 +00:00
rotateRightIfNeeded : Dict k v -> Dict k v
2012-10-10 21:37:42 +00:00
rotateRightIfNeeded t =
case t of
RBNode _ _ _ (RBNode Red _ _ (RBNode Red _ _ _ _) _) _ -> rotateRight t
_ -> t
2012-10-10 21:37:42 +00:00
otherColor c = case c of { Red -> Black ; Black -> Red }
color_flip : Dict k v -> Dict k v
2012-10-10 21:37:42 +00:00
color_flip t =
case t of
RBNode c1 bk bv (RBNode c2 ak av la ra) (RBNode c3 ck cv lc rc) ->
RBNode (otherColor c1) bk bv
(RBNode (otherColor c2) ak av la ra)
(RBNode (otherColor c3) ck cv lc rc)
_ -> Error.raise "color_flip called on a Empty or Node with a Empty child"
2012-10-10 21:37:42 +00:00
color_flipIfNeeded : Dict k v -> Dict k v
color_flipIfNeeded t =
2012-10-10 21:37:42 +00:00
case t of
RBNode _ _ _ (RBNode Red _ _ _ _) (RBNode Red _ _ _ _) -> color_flip t
_ -> t
2012-10-10 21:37:42 +00:00
fixUp t = color_flipIfNeeded (rotateRightIfNeeded (rotateLeftIfNeeded t))
ensureBlackRoot : Dict k v -> Dict k v
ensureBlackRoot t =
2012-10-10 21:37:42 +00:00
case t of
RBNode Red k v l r -> RBNode Black k v l r
_ -> t
-- Insert a key-value pair into a dictionary. Replaces value when there is
-- a collision.
insert : Comparable k -> v -> Dict (Comparable k) v -> Dict (Comparable k) v
insert k v t = -- Invariant: t is a valid left-leaning rb tree
2012-10-10 21:37:42 +00:00
let ins t =
case t of
RBEmpty -> RBNode Red k v RBEmpty RBEmpty
RBNode c k' v' l r ->
2012-10-10 21:37:42 +00:00
let h = case compare k k' of
LT -> RBNode c k' v' (ins l) r
EQ -> RBNode c k' v l r -- replace
GT -> RBNode c k' v' l (ins r)
in fixUp h
in ensureBlackRoot (ins t)
{--
if not (invariants_hold t) then
Error.raise "invariants broken before insert"
2012-10-10 21:37:42 +00:00
else (let new_t = ensureBlackRoot (ins t) in
if not (invariants_hold new_t) then
Error.raise "invariants broken after insert"
2012-10-10 21:37:42 +00:00
else new_t)
--}
2012-10-10 21:37:42 +00:00
-- Create a dictionary with one key-value pair.
singleton : Comparable k -> v -> Dict (Comparable k) v
singleton k v = insert k v RBEmpty
2012-10-10 21:37:42 +00:00
isRed : Dict k v -> Bool
2012-10-10 21:37:42 +00:00
isRed t =
case t of
RBNode Red _ _ _ _ -> True
_ -> False
2012-10-10 21:37:42 +00:00
isRedLeft : Dict k v -> Bool
2012-10-10 21:37:42 +00:00
isRedLeft t =
case t of
RBNode _ _ _ (RBNode Red _ _ _ _) _ -> True
_ -> False
2012-10-10 21:37:42 +00:00
isRedLeftLeft : Dict k v -> Bool
2012-10-10 21:37:42 +00:00
isRedLeftLeft t =
case t of
RBNode _ _ _ (RBNode _ _ _ (RBNode Red _ _ _ _) _) _ -> True
_ -> False
2012-10-10 21:37:42 +00:00
isRedRight : Dict k v -> Bool
2012-10-10 21:37:42 +00:00
isRedRight t =
case t of
RBNode _ _ _ _ (RBNode Red _ _ _ _) -> True
_ -> False
2012-10-10 21:37:42 +00:00
isRedRightLeft : Dict k v -> Bool
2012-10-10 21:37:42 +00:00
isRedRightLeft t =
case t of
RBNode _ _ _ _ (RBNode _ _ _ (RBNode Red _ _ _ _) _) -> True
_ -> False
2012-10-10 21:37:42 +00:00
moveRedLeft : Dict k v -> Dict k v
moveRedLeft t =
2012-10-10 21:37:42 +00:00
let t' = color_flip t in
case t' of
RBNode c k v l r ->
2012-10-10 21:37:42 +00:00
case r of
RBNode _ _ _ (RBNode Red _ _ _ _) _ ->
color_flip (rotateLeft (RBNode c k v l (rotateRight r)))
_ -> t'
_ -> t'
2012-10-10 21:37:42 +00:00
moveRedRight : Dict k v -> Dict k v
2012-10-10 21:37:42 +00:00
moveRedRight t =
let t' = color_flip t in
if isRedLeftLeft t' then color_flip (rotateRight t') else t'
moveRedLeftIfNeeded : Dict k v -> Dict k v
moveRedLeftIfNeeded t =
2013-05-06 09:30:50 +00:00
if isRedLeft t || isRedLeftLeft t then t else moveRedLeft t
2012-10-10 21:37:42 +00:00
moveRedRightIfNeeded : Dict k v -> Dict k v
moveRedRightIfNeeded t =
2013-05-06 09:30:50 +00:00
if isRedRight t || isRedRightLeft t then t else moveRedRight t
2012-10-10 21:37:42 +00:00
deleteMin : Dict k v -> Dict k v
deleteMin t =
2012-10-10 21:37:42 +00:00
let del t =
case t of
RBNode _ _ _ RBEmpty _ -> RBEmpty
_ -> case moveRedLeftIfNeeded t of
RBNode c k v l r -> fixUp (RBNode c k v (del l) r)
RBEmpty -> RBEmpty
2012-10-10 21:37:42 +00:00
in ensureBlackRoot (del t)
{--
2012-10-10 21:37:42 +00:00
deleteMax t =
let del t =
let t' = if isRedLeft t then rotateRight t else t in
case t' of
{ RBNode _ _ _ _ RBEmpty -> RBEmpty
2012-10-10 21:37:42 +00:00
; _ -> let t'' = moveRedRightIfNeeded t' in
case t'' of
{ RBNode c k v l r -> fixUp (RBNode c k v l (del r))
; RBEmpty -> RBEmpty } }
2012-10-10 21:37:42 +00:00
in ensureBlackRoot (del t)
--}
2012-10-10 21:37:42 +00:00
-- Remove a key-value pair from a dictionary. If the key is not found,
-- no changes are made.
remove : Comparable k -> Dict (Comparable k) v -> Dict (Comparable k) v
remove k t =
let eq_and_noRightNode t =
case t of { RBNode _ k' _ _ RBEmpty -> k == k' ; _ -> False }
eq t = case t of { RBNode _ k' _ _ _ -> k == k' ; _ -> False }
delLT t = case moveRedLeftIfNeeded t of
RBNode c k' v l r -> fixUp (RBNode c k' v (del l) r)
RBEmpty -> Error.raise "delLT on Empty"
delEQ t = case t of -- Replace with successor
RBNode c _ _ l r -> let (k',v') = min r in
fixUp (RBNode c k' v' l (deleteMin r))
RBEmpty -> Error.raise "delEQ called on a Empty"
delGT t = case t of
RBNode c k' v l r -> fixUp (RBNode c k' v l (del r))
RBEmpty -> Error.raise "delGT called on a Empty"
del t = case t of
RBEmpty -> RBEmpty
RBNode _ k' _ _ _ ->
if k < k' then delLT t else
let u = if isRedLeft t then rotateRight t else t in
if eq_and_noRightNode u then RBEmpty else
let t' = moveRedRightIfNeeded t in
if eq t' then delEQ t' else delGT t'
in if member k t then ensureBlackRoot (del t) else t
{--
if not (invariants_hold t) then
Error.raise "invariants broken before remove"
2012-10-10 21:37:42 +00:00
else (let t' = ensureBlackRoot (del t) in
if invariants_hold t' then t' else
Error.raise "invariants broken after remove")
--}
-- Apply a function to all values in a dictionary.
map : (a -> b) -> Dict (Comparable k) a -> Dict (Comparable k) b
map f t =
case t of
RBEmpty -> RBEmpty
RBNode c k v l r -> RBNode c k (f v) (map f l) (map f r)
2012-10-10 21:37:42 +00:00
-- Fold over the key-value pairs in a dictionary, in order from lowest
-- key to highest key.
foldl : (Comparable k -> v -> b -> b) -> b -> Dict (Comparable k) v -> b
foldl f acc t =
case t of
RBEmpty -> acc
RBNode _ k v l r -> foldl f (f k v (foldl f acc l)) r
-- Fold over the key-value pairs in a dictionary, in order from highest
-- key to lowest key.
foldr : (Comparable k -> v -> b -> b) -> b -> Dict (Comparable k) v -> b
foldr f acc t =
2012-10-10 21:37:42 +00:00
case t of
RBEmpty -> acc
RBNode _ k v l r -> foldr f (f k v (foldr f acc r)) l
-- Combine two dictionaries. If there is a collision, preference is given
-- to the first dictionary.
union : Dict (Comparable k) v -> Dict (Comparable k) v -> Dict (Comparable k) v
union t1 t2 = foldl insert t2 t1
-- Keep a key-value pair when its key appears in the second dictionary.
-- Preference is given to values in the first dictionary.
intersect : Dict (Comparable k) v -> Dict (Comparable k) v -> Dict (Comparable k) v
intersect t1 t2 =
let combine k v t = if k `member` t2 then insert k v t else t
in foldl combine empty t1
-- Keep a key-value pair when its key does not appear in the second dictionary.
-- Preference is given to the first dictionary.
diff : Dict (Comparable k) v -> Dict (Comparable k) v -> Dict (Comparable k) v
diff t1 t2 = foldl (\k v t -> remove k t) t1 t2
-- Get all of the keys in a dictionary.
keys : Dict (Comparable k) v -> [Comparable k]
keys t = foldr (\k v acc -> k :: acc) [] t
-- Get all of the values in a dictionary.
values : Dict (Comparable k) v -> [v]
values t = foldr (\k v acc -> v :: acc) [] t
-- Convert a dictionary into an association list of key-value pairs.
toList : Dict (Comparable k) v -> [(Comparable k,v)]
toList t = foldr (\k v acc -> (k,v) :: acc) [] t
-- Convert an association list into a dictionary.
fromList : [(Comparable k,v)] -> Dict (Comparable k) v
fromList assocs = List.foldl (\(k,v) d -> insert k v d) empty assocs