roc/examples/effect/RBTree.roc
2021-01-04 15:18:49 -05:00

353 lines
9.1 KiB
Plaintext

interface RBTree exposes [ Dict, empty, size, singleton, isEmpty, insert, remove, update, fromList, toList, balance ] imports []
# The color of a node. Leaves are considered Black.
NodeColor : [ Red, Black ]
Dict k v : [ Node NodeColor k v (Dict k v) (Dict k v), Empty ]
Key k : Num k
Maybe a : [ Just a, Nothing ]
# Create an empty dictionary.
empty : Dict k v
empty =
Empty
# Create a dictionary with one key-value pair.
singleton : Key k, v -> Dict (Key k) v
singleton = \key, value ->
# Root node is always Black
Node Black key value Empty Empty
# {-| Determine the number of key-value pairs in the dictionary. -}
size : Dict k v -> Int *
size = \dict ->
sizeHelp 0 dict
sizeHelp : Int *, Dict k v -> Int *
sizeHelp = \n, dict ->
when dict is
Empty ->
n
Node _ _ _ left right ->
sizeHelp (sizeHelp (n+1) right) left
isEmpty : Dict k v -> Bool
isEmpty = \dict ->
when dict is
Empty ->
True
Node _ _ _ _ _ ->
False
insert : Key k, v, Dict (Key k) v -> Dict (Key k) v
insert = \key, value, dict ->
when insertHelp key value dict is
Node Red k v l r ->
Node Black k v l r
x ->
x
insertHelp : (Key k), v, Dict (Key k) v -> Dict (Key k) v
insertHelp = \key, value, dict ->
when dict is
Empty ->
# New nodes are always red. If it violates the rules, it will be fixed
# when balancing.
Node Red key value Empty Empty
Node nColor nKey nValue nLeft nRight ->
when Num.compare key nKey is
LT ->
balance nColor nKey nValue (insertHelp key value nLeft) nRight
EQ ->
Node nColor nKey value nLeft nRight
GT ->
balance nColor nKey nValue nLeft (insertHelp key value nRight)
balance : NodeColor, k, v, Dict k v, Dict k v -> Dict k v
balance = \color, key, value, left, right ->
when right is
Node Red rK rV rLeft rRight ->
when left is
Node Red lK lV lLeft lRight ->
Node
Red
key
value
(Node Black lK lV lLeft lRight)
(Node Black rK rV rLeft rRight)
_ ->
Node color rK rV (Node Red key value left rLeft) rRight
_ ->
when left is
Node Red lK lV (Node Red llK llV llLeft llRight) lRight ->
Node
Red
lK
lV
(Node Black llK llV llLeft llRight)
(Node Black key value lRight right)
_ ->
Node color key value left right
remove : Key k, Dict (Key k) v -> Dict (Key k) v
remove = \key, dict ->
# Root node is always Black
when removeHelp key dict is
Node Red k v l r ->
Node Black k v l r
x ->
x
# The easiest thing to remove from the tree, is a red node. However, when searching for the
# node to remove, we have no way of knowing if it will be red or not. This remove implementation
# makes sure that the bottom node is red by moving red colors down the tree through rotation
# and color flips. Any violations this will cause, can easily be fixed by balancing on the way
# up again.
removeHelp : Key k, Dict (Key k) v -> Dict (Key k) v
removeHelp = \targetKey, dict ->
when dict is
Empty ->
Empty
Node color key value left right ->
if targetKey < key then
when left is
Node Black _ _ lLeft _ ->
when lLeft is
Node Red _ _ _ _ ->
Node color key value (removeHelp targetKey left) right
_ ->
when moveRedLeft dict is
Node nColor nKey nValue nLeft nRight ->
balance nColor nKey nValue (removeHelp targetKey nLeft) nRight
Empty ->
Empty
_ ->
Node color key value (removeHelp targetKey left) right
else
removeHelpEQGT targetKey (removeHelpPrepEQGT targetKey dict color key value left right)
removeHelpPrepEQGT : Key k, Dict (Key k) v, NodeColor, (Key k), v, Dict (Key k) v, Dict (Key k) v -> Dict (Key k) v
removeHelpPrepEQGT = \_, dict, color, key, value, left, right ->
when left is
Node Red lK lV lLeft lRight ->
Node
color
lK
lV
lLeft
(Node Red key value lRight right)
_ ->
when right is
Node Black _ _ (Node Black _ _ _ _) _ ->
moveRedRight dict
Node Black _ _ Empty _ ->
moveRedRight dict
_ ->
dict
# When we find the node we are looking for, we can remove by replacing the key-value
# pair with the key-value pair of the left-most node on the right side (the closest pair).
removeHelpEQGT : Key k, Dict (Key k) v -> Dict (Key k) v
removeHelpEQGT = \targetKey, dict ->
when dict is
Node color key value left right ->
if targetKey == key then
when getMin right is
Node _ minKey minValue _ _ ->
balance color minKey minValue left (removeMin right)
Empty ->
Empty
else
balance color key value left (removeHelp targetKey right)
Empty ->
Empty
getMin : Dict k v -> Dict k v
getMin = \dict ->
when dict is
# Node _ _ _ ((Node _ _ _ _ _) as left) _ ->
Node _ _ _ left _ ->
when left is
Node _ _ _ _ _ -> getMin left
_ -> dict
_ ->
dict
moveRedLeft : Dict k v -> Dict k v
moveRedLeft = \dict ->
when dict is
# Node clr k v (Node lClr lK lV lLeft lRight) (Node rClr rK rV ((Node Red rlK rlV rlL rlR) as rLeft) rRight) ->
# Node clr k v (Node lClr lK lV lLeft lRight) (Node rClr rK rV rLeft rRight) ->
Node clr k v (Node _ lK lV lLeft lRight) (Node _ rK rV rLeft rRight) ->
when rLeft is
Node Red rlK rlV rlL rlR ->
Node
Red
rlK
rlV
(Node Black k v (Node Red lK lV lLeft lRight) rlL)
(Node Black rK rV rlR rRight)
_ ->
when clr is
Black ->
Node
Black
k
v
(Node Red lK lV lLeft lRight)
(Node Red rK rV rLeft rRight)
Red ->
Node
Black
k
v
(Node Red lK lV lLeft lRight)
(Node Red rK rV rLeft rRight)
_ ->
dict
moveRedRight : Dict k v -> Dict k v
moveRedRight = \dict ->
when dict is
Node _ k v (Node _ lK lV (Node Red llK llV llLeft llRight) lRight) (Node _ rK rV rLeft rRight) ->
Node
Red
lK
lV
(Node Black llK llV llLeft llRight)
(Node Black k v lRight (Node Red rK rV rLeft rRight))
Node clr k v (Node _ lK lV lLeft lRight) (Node _ rK rV rLeft rRight) ->
when clr is
Black ->
Node
Black
k
v
(Node Red lK lV lLeft lRight)
(Node Red rK rV rLeft rRight)
Red ->
Node
Black
k
v
(Node Red lK lV lLeft lRight)
(Node Red rK rV rLeft rRight)
_ ->
dict
removeMin : Dict k v -> Dict k v
removeMin = \dict ->
when dict is
Node color key value left right ->
when left is
Node lColor _ _ lLeft _ ->
when lColor is
Black ->
when lLeft is
Node Red _ _ _ _ ->
Node color key value (removeMin left) right
_ ->
when moveRedLeft dict is
Node nColor nKey nValue nLeft nRight ->
balance nColor nKey nValue (removeMin nLeft) nRight
Empty ->
Empty
_ ->
Node color key value (removeMin left) right
_ ->
Empty
_ ->
Empty
# Update the value of a dictionary for a specific key with a given function.
update : Key k, (Maybe v -> Maybe v), Dict (Key k) v -> Dict (Key k) v
update = \targetKey, alter, dictionary ->
when alter (get targetKey dictionary) is
Just value ->
insert targetKey value dictionary
Nothing ->
remove targetKey dictionary
get : Key k, Dict (Key k) v -> Maybe v
get = \targetKey, dict ->
when dict is
Empty ->
Nothing
Node _ key value left right ->
when Num.compare targetKey key is
LT ->
get targetKey left
EQ ->
Just value
GT ->
get targetKey right
fromList : List {key : Num k, value : v } -> Dict (Num k) v
fromList = \xs ->
List.walkRight xs (\{key, value}, dict -> insert key value dict) empty
foldr : (k, v, b -> b), b, Dict k v -> b
foldr = \func, acc, t ->
when t is
Empty ->
acc
Node _ key value left right ->
foldr func (func key value (foldr func acc right)) left
# Convert a dictionary into an association list of key-value pairs, sorted by keys.
toList : Dict k v -> List { key : k, value : v }
toList = \dict ->
foldr (\key, value, list -> List.append list {key,value}) [] dict