[Haskell-cafe] asymmetric runtimes for symmetric trees
Daniel Fischer
daniel.is.fischer at web.de
Tue Sep 21 15:56:03 EDT 2010
On Tuesday 21 September 2010 19:45:50, Daniel Seidel wrote:
> Hi,
>
> I've got the answer - its the sumLeafs function that behaves different
> for leftPath / rightPath. If one exchanges it in the leftPath case by
>
> sumLeafs (Fork l r) = sumLeafs r + sumLeafs l
> sumLeafs (Leaf i) = i
>
> the difference in runtime is gone.
Yes. The matter is that with the original sumLeafs (btw., that ought to be
sumLeaves), the left path builds a call-tree for sumLeafs isomorphic to the
tree first. Once that's complete, the evaluation proceeds from leaves to
root. The right path, on the other hand, always has the call to sumLeafs
for the left subtree returning without recursion, so it builds a tree of
calls to (+) which is isomorphic to the original tree,
1 + (1 + (1 + (1 + ...(1 + 1)...))).
Since (+) is a simpler function than sumLeafs, that call tree is apparently
cheaper.
For trees like these, a much better implementation of sumLeafs uses an
accumulating parameter and detects leaves on the right while going down the
tree:
sumLeafs = smlvs 0
smlvs !acc (Leaf i) = acc+i
smlvs acc (Fork l (Leaf j)) = smlvs (acc+j) l
smlvs acc (Fork l r) = smlvs (smlvs acc l) r
(Note there's no need to detect leaves on the left.)
That sums the left and right paths in 0.02s with -O2.
It'll be faster (and allocate much less) for all trees containing many very
skewed subtrees (so lots of leaves with the other branch going much
deeper), but for well balanced trees, it makes practically no difference.
>
> Cheers,
>
> Daniel.
>
Hey, that's *my* line!
More information about the Haskell-Cafe
mailing list