Ленивое дерево с утечкой пространства

Я пишу программу, пытаясь реализовать игрушечный 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 должен избавляться от дочерних узлов сразу после их обхода.

Есть ли способ исправить такую ​​утечку пространства?

10
задан akborder 9 July 2011 в 23:17
поделиться