Here's the test case...

The space profile for tests 2 and 3 look interesting;
there are n triangular "spikes" (where 'n' is the breadth
of the tree) that drop off sharply.

My hypothesis is that 'deserialize' (the problematic function,
called 'buildTree' in my earlier message) is building up
a long chain of suspensions of the form

        snd . snd . snd . ... build 

that aren't getting reduced... not sure about this though.

It takes a really large tree before Hugs runs out of space
with this test case (breadth=11, depth=6 or so).  In my
real program though there's much more data stored at each
node and it fails on modestly-sized inputs.

Thanks in advance for any advice...

--Joe English

  [EMAIL PROTECTED]

--

module SpaceLeak where

data Tree a = Tree a [Tree a]
        deriving (Show, Eq)

--
-- a few of the usual polytypic functions...
--
mapTree                 :: (a -> b) -> Tree a -> Tree b
mapTree f (Tree a c)    =  Tree (f a) (map (mapTree f) c)

type TreeF a b          =  (a, [b])
cataTree                :: (TreeF a b -> b) -> Tree a -> b
anaTree                 :: (b -> TreeF a b) -> b -> Tree a
cataTree f (Tree a c)   =  f (a,map (cataTree f) c)
anaTree g b             =  let (a,bs) = g b in Tree a (map (anaTree g) bs)

--
-- and a few useful utilies...
--
preorderTree :: Tree a -> [a]
preorderTree t = traverse t [] where
    traverse (Tree a c) k       = a : travlist c k
    travlist (c:cs) k           = traverse c (travlist cs k)
    travlist [] k               = k

sizeTree :: Tree a -> Integer
sizeTree = cataTree (\(_,l)-> 1 + sum l)

treeChildren (Tree n c) =  c

--
-- A datatype for tree serialization:
-- This is similar to the tokens returned by an XML parser.
--
data Event a =
    StartTag a
  | Data a
  | EndTag
    deriving (Show,Eq)

--
-- serialize turns a tree into a list of start/data/end events.
--
serialize               :: Tree a -> [Event a]
serialize t = stree t [] where
    stree (Tree x []) k = Data x : k
    stree (Tree x l) k  = StartTag x : slist l (EndTag : k)
    slist [] k          = k
    slist (t:ts) k      = stree t (slist ts k)

--
-- deserialize builds a tree from a list of events;
-- (deserialize . serialize) = id
--
-- This is the problematic function.
--
deserialize :: [Event a] -> Tree a
deserialize events = head (fst (build events)) where
    build :: [Event a] -> ([Tree a], [Event a])
    build [] = ([],[])
    build (e:es) = case e of
        Data a ->
            let (siblings, rest) = build es
            in  (Tree a [] : siblings, rest)
        StartTag a ->
            let (children,es')  = build es
                (siblings,rest) = build es'
            in (Tree a children : siblings, rest)
        EndTag -> ([],es)

--
-- 'sampleTree breadth depth' generates a tree of the specified depth,
-- where each non-leaf node node has 'breadth' children.
--
testTree breadth depth =  anaTree testCOAlg depth  where
        testCOAlg n = (n, if n > 1 then take breadth $ repeat (n-1) else [])

--
-- Quick sanity check to make sure 'deserialize' works as specified:
--
test0 n m =  testTree n m == (deserialize . serialize) (testTree n m) -- True

--
-- The following all run in bounded space:
-- try with ':set -d1000' in Hugs, n=4, m=6 or so...
-- In particular,  serialize $ testTree n m behaves nicely.
--
test1a n m = sizeTree $ testTree n m
test1b n m = length   $ preorderTree $ testTree n m
test1c n m = length   $ serialize    $ testTree n m

--
-- These seem to leak space:
--
test2a n m = sizeTree $ deserialize $ serialize $ testTree n m
test2b n m = length   $ preorderTree $ deserialize $ serialize $ testTree n m

test3a n m = deserialize $ serialize $ testTree n m
test3b n m = preorderTree $ deserialize $ serialize $ testTree n m

-- This does not:
test4a n m = length $ treeChildren $ deserialize $ serialize $ testTree n m
-- But this does:
test4b n m = map (length . treeChildren) $ treeChildren 
             $ deserialize $serialize $ testTree n m 

-- *EOF* --

Reply via email to