Estoy escribiendo un programa que intenta implementar un procesador XML de juguete. En este momento se supone que el programa debe leer un flujo de eventos (piense en SAX) describiendo la estructura de un documento y construyendo perezosamente el árbol correspondiente.Árbol difuso con una fuga de espacio
Los eventos se definen mediante la siguiente tipo de datos:
data Event = Open String
| Close
Una posible entrada entonces sería:
[Open "a", Open "b", Close, Open "c", Close, Close]
que correspondería al árbol:
a
/\
b c
lo haría gusta generar el árbol de una manera perezosa, por lo que no necesita estar presente en la memoria en forma completa en cualquier momento. Mi implementación actual, sin embargo, parece tener una fuga de espacio que hace que se conserven todos los nodos, incluso cuando ya no son necesarios. Aquí está el código:
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
La función convierte la lista de eventos en una lista de Tree Node
. getChildren
recoge todos los nodos secundarios (etiquetados como "b") de la raíz ("a"). Estos se cuentan y el número resultante se imprime.
El programa compilado, construido con GHC 7.0.4 (-O2), sigue aumentando su uso de memoria hasta el punto en que imprime el recuento de nodos. Esperaba, por otro lado, un uso de memoria casi constante.
En cuanto al perfil del montón "-hd", está claro que la mayoría de la memoria la toma el constructor de lista (:
). Parece que una de las listas producidas por scan
o por trees
se conserva por completo. No entiendo por qué, sin embargo, como length . getChildren
debería deshacerse de los nodos secundarios tan pronto como se atraviesan.
¿Hay alguna forma de solucionar dicha fuga de espacio?
¿Podría publicar el código para 'splitStream' también? Puede ser demasiado estricto. – luqui
Por cierto, recomendaría '' Control.Arrow.first' para scan': en lugar de 'vamos (b, a) = exploración (profundidad + 1) ss en (s: b, a)', se puede decir 'primero (s :) (escaneo (profundidad + 1) ss) '. – luqui
@luqui: está ahí, entre 'trees' y' scan'. Solo una línea, así que 'splitStream' es fácil pasar por alto. –