У меня есть следующее красное черное дерево:
data Tree a
= E
| S a
| C !Color !(Tree a) !(Tree a)
data Color = R | B
В случае этого дерева все данные хранятся в листьях (конструктор S). Я написал функцию insert
наподобие стандартных красно-черных деревьев Окасаки[1] (изменив части, в которых значения хранятся во внутренних узлах).
В этом случае я заполняю дерево 10 миллионами элементов:
l = go 10000000 E
where
go 0 t = insert 0 t
go n t = insert t $ go (n - 1) t
Когда я пытаюсь оценить самый левый элемент (лист) дерева следующим образом:
left :: Tree a -> Maybe a
left E = Nothing
left (S x) = Just x
left (C _ _ l _) = left l
Я сталкиваюсь со следующим:
левый л
*** Исключение: переполнение стека
Это связано с тем, как я строю дерево (не хвостовое рекурсивное), или есть какая-то недостающая утечка пространства, которую я не вижу.
Обратите внимание, что функция отлично работает для миллиона элементов. Кроме того, я попытался использовать хвостовой рекурсивный способ построения дерева:
l = go 10000000 E
where
go 0 t = insert 0 t
go n t = go (n - 1) (insert n t)
но столкнулся с тем же исключением переполнения стека.
[1] https://www.cs.tufts.edu/%7Enr/cs257/archive/chris-okasaki/redblack99.pdf
ИЗМЕНИТЬ
Функция вставки и баланса для полноты:
insert :: Ord a => a -> Tree a -> Tree a
insert x xs = makeBlack $ ins xs
where
ins E = S x
ins (S a) = C R (S x) (S a)
ins (C c l r) = balance c (ins l) r -- always traverse left and trust the balancing
makeBlack (C _ l r) = C B l r
makeBlack a = a
balance :: Color -> Tree a -> Tree a -> Tree a
balance B (C R (C R a b) c) d = C R (C B a b) (C B c d)
balance B (C R a (C R b c)) d = C R (C B a b) (C B c d)
balance B a (C R (C R b c) d) = C R (C B a b) (C B c d)
balance B a (C R b (C R c d)) = C R (C B a b) (C B c d)
balance color a b = C color a b
С моей стороны была опечатка при вводе кода вставки, это insert n $ go (n - 1) t
, а не insert t $ go (n - 1) t
. Однако, когда на самом деле возникло переполнение стека, код был правильным, и переполнение произошло в ghci.