Я пишу программу, пытающуюся реализовать игрушечный XML-процессор. Прямо сейчас программа должна читать поток событий (думаю SAX), описывая структуру документа и лениво строить соответствующее дерево.
События определяются следующим типом данных:
data Event = Open String
| Close
Возможный ввод:
[Open "a", Open "b", Close, Open "c", Close, Close]
который будет соответствовать дереву:
a
/ \
b c
Я хотел бы генерировать дерево ленивым способом, так что он не должен присутствовать в памяти в полной форме в любое время. Однако у моей текущей реализации, похоже, есть утечка пространства, в результате чего все узлы сохраняются, даже когда они больше не нужны. Вот код:
data Event = Open String
| Close
data Tree a = Tree a (Trees a)
type Trees a = [Tree a]
data Node = Node String
trees [] = []
trees (Open x : es) =
let (children, rest) = splitStream es
in (Tree (Node x) (trees children)) : (trees rest)
splitStream es = scan 1 es
scan depth ([email protected](Open {}) : ss) =
let (b, a) = scan (depth+1) ss
in (s:b, a)
scan depth ([email protected] : ss) =
case depth of
1 -> ([], ss)
x -> let (b, a) = scan (depth-1) ss
in (s:b, a)
getChildren = concatMap loop
where
loop (Tree _ cs) = cs
main = print .
length .
getChildren .
trees $
[Open "a"] ++ (concat . replicate 1000000 $ [Open "b", Close]) ++ [Close]
Функция trees
преобразует список событий в список Tree Node
. getChildren
собирает все дочерние узлы (помеченные как "b" ) корня ( "a" ). Затем они подсчитываются и выводится результирующее число.
Скомпилированная программа, построенная с помощью GHC 7.0.4 (-O2), продолжает увеличивать объем использования памяти до момента, когда печатает счетчик node. С другой стороны, я ожидал почти постоянного использования памяти.
Глядя на профиль "-hd" кучи, ясно, что большую часть памяти занимает конструктор списка (:
). Кажется, что один из списков, созданных scan
или trees
, сохраняется полностью. Однако я не понимаю, почему, поскольку length . getChildren
должен избавиться от дочерних узлов, как только они пройдут.
Есть ли способ исправить такую утечку пространства?