[Haskell-cafe] Re: A guess on stack-overflows - thunks build-up andtail recursion

Claus Reinke claus.reinke at talk21.com
Mon Mar 30 08:24:53 EDT 2009


>I wonder if I could write some sort of "chunked fold" which basically 
>still produces the same amount of thunks but in a way so that the do not 
>go on the stack all at once for reduction and thus do not cause a stack 
>overflow. Kind of a tree.

Not without re-associating the applications of the operation being folded.
It is the association of everything to one side, for a strict operation, that 
leads to an expression whose evaluation will run out of limited stack,
while storing the arguments not yet used on the other side.

If your operation is associative, you could unroll the recursion a few steps
and reassociate, thereby reducing the depth of the stack of left-overs by 
the factor of unrolling. Or you could try building a tree of applications, 
perhaps from the bottom up:

import Debug.Observe

foldr2 op n (a:b:t) = (a`op`b) `op` foldr2 op n t
foldr2 op n [a]     = a `op` n
foldr2 op n []      = n

foldl2 op n (a:b:t) = foldl2 op (n`op`(a`op`b)) t
foldl2 op n [a]     = n `op` a
foldl2 op n []      = n

foldlt op n []  = n
foldlt op n [a] = n `op` a
foldlt op n l   = foldlt op n (pairs l)
  where pairs []      = []
        pairs [a]     = [a]
        pairs (a:b:t) = (a `op` b) : pairs t

main = 
  -- printO $ observe "foldl2" foldl2 (+) 0 [1..10::Int]
  -- printO $ observe "foldr2" foldr2 (+) 0 [1..10::Int]
  printO $ observe "foldlt" foldlt (+) 0 [1..10::Int]

That works somewhat better for foldl2 than for foldr2 (why?), and 
foldlt holds out the longest (is it possible to write a foldrt?), but isn't 
all that efficient:

*Main> foldl (+) 0 [1..500000::Int]
446198416
*Main> foldl (+) 0 [1..600000::Int]
*** Exception: stack overflow
*Main> foldl2 (+) 0 [1..600000::Int]
-388326432
*Main> foldl2 (+) 0 [1..1000000::Int]
1784293664
*Main> foldl2 (+) 0 [1..1100000::Int]
*** Exception: stack overflow

*Main> foldr (+) 0 [1..500000::Int]
446198416
*Main> foldr (+) 0 [1..600000::Int]
*** Exception: stack overflow
*Main> foldr2 (+) 0 [1..600000::Int]
-388326432
*Main> foldr2 (+) 0 [1..700000::Int]
*** Exception: stack overflow

These versions still have their issues, as observation on small lists
shows, so you might want to experiment with your own variations;-)

Claus



More information about the Haskell-Cafe mailing list