I'm trying to implement a left leaning red black tree as described here. This is their snippet for insert
private Node insert(Node h, Key key, Value value) {
if (h == null) return new Node(key, value);
if (isRed(h.left) && isRed(h.right)) colorFlip(h);
int cmp = key.compareTo(h.key);
if (cmp == 0) h.val = value;
else if (cmp < 0) h.left = insert(h.left, key, value);
else h.right = insert(h.right, key, value);
if (isRed(h.right) && !isRed(h.left)) h = rotateLeft(h);
if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h);
return h;
}
My attempt at a port in Haskell is... quite ugly, with a lot of repetition. I think it's because I'm still thinking procedurally. Any feedback on what I should do differently? Or is there no way around having many next-state variables (x', x'', x'''
)? Should I be approaching this completely differently?
data Colour = Red | Black deriving (Show)
data Tree a
= Branch (Tree a) a (Tree a) Colour
| Leaf
deriving (Show)
add :: (Ord a) => Tree a -> a -> Tree a
add tree val
= let
(Branch left' node' right' _) = fix_up $ do_add tree val
in (Branch left' node' right' Black) -- root always black
do_add :: (Ord a) => Tree a -> a -> Tree a
do_add (Branch left node right colour) val
| val < node = (Branch (add left val) node right colour)
| val > node = (Branch left node (add right val) colour)
| otherwise = (Branch left node right colour)
do_add Leaf val = (Branch Leaf val Leaf Black)
get_left_node :: Tree a -> Tree a
get_left_node (Branch left _ _ _) = left
get_left_node Leaf = Leaf
fix_up :: Tree a -> Tree a
fix_up (Branch left node right colour)
= let
branch' = if ((not (is_red left)) && (is_red right)) then (rotate_left (Branch left node right colour)) else (Branch left node right colour)
(Branch left' _ right' _) = branch'
branch'' = if ((is_red left') && (is_red (get_left_node left'))) then (rotate_right branch') else branch'
(Branch left'' _ right'' _) = branch''
branch''' = if ((is_red left'') && (is_red right'')) then (flip_colours branch'') else branch''
in branch'''
rotate_left :: Tree a -> Tree a
rotate_left (Branch left node (Branch right_left right_node right_right right_colour) colour)
= let
left' = (Branch left node right_left Red)
centre' = (Branch left' right_node right_right colour)
in centre'
rotate_right :: Tree a -> Tree a
rotate_right (Branch (Branch left_left left_node left_right left_colour) node right colour)
= let
right' = (Branch left_right node right Red)
centre' = (Branch left_left left_node right' colour)
in centre'
flip_colours :: Tree a -> Tree a
flip_colours (Branch (Branch left_left left_node left_right left_colour) node (Branch right_left right_node right_right right_colour) colour) = let
left' = (Branch left_left left_node left_right (invert_colour left_colour))
right' = (Branch right_left right_node right_right (invert_colour right_colour))
centre' = (Branch left' node right' (invert_colour colour))
in centre'
is_red :: Tree a -> Bool
is_red (Branch _ _ _ Red) = True
is_red _ = False
is_black :: Tree a -> Bool
is_black node = not $ is_red node
fix_up
it's very much procedural \$\endgroup\$