Я пишу программу, пытаясь реализовать игрушечный XML-процессор. Прямо сейчас программа должна читать поток событий (вспомним SAX), описывающий структуру документа, и лениво строить соответствующее дерево.
События определяются следующим типом данных:
data Event = Open String
| Close
Возможный ввод будет тогда be:
[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 (s@(Open {}) : ss) =
let (b, a) = scan (depth+1) ss
in (s:b, a)
scan depth (s@Close : 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]
Функция tree
преобразует список событий в список Tree Node
. getChildren
собирает все дочерние узлы (помеченные «b») корня («a»). Затем они подсчитываются, и полученное число выводится на печать.
Скомпилированная программа, созданная с помощью GHC 7.0.4 (-O2), продолжает увеличивать использование памяти до момента, когда она печатает количество узлов. С другой стороны, я ожидал почти постоянного использования памяти.
Глядя на профиль кучи «-hd», становится ясно, что большая часть памяти занята конструктором списка (:
). Похоже, что один из списков, созданных сканированием
или деревьями
, сохраняется полностью. Я не понимаю, почему, однако, как длина. getChildren
должен избавляться от дочерних узлов сразу после их обхода.
Есть ли способ исправить такую утечку пространства?