如何解决haskell中的“堆栈空间溢出”

时间:2009-08-18 06:53:23

标签: haskell recursion lazy-evaluation

运行以下程序将打印“空间溢出:当前大小为8388608字节”。我看过thisthis,但仍然不知道如何解决我的问题。我正在使用foldr,不应该保证它是“尾递归”吗?

到目前为止,我对Haskell感觉很棒,直到我知道在使用强大的递归时我应该防止“空间溢出”。 :)

module Main where
import Data.List

value a  b = 
  let l = length $ takeWhile (isPrime) $ map (\n->n^2 + a * n + b) [0..]
  in (l, a ,b)

euler27 = let tuple_list = [value a b | a <-[-999..999] , b <- [-999..999]]
      in foldr (\(n,a,b) (max,v) -> if n > max then (n , a * b) else (max ,v) ) (0,0) tuple_list
main = print euler27

编辑:为简单起见删除isPrime的定义

2 个答案:

答案 0 :(得分:11)

正如皮尔回答的那样,你应该使用foldl'。有关详细信息:

  • foldl'计算其“左侧”,然后再将其放入折叠步骤。
  • foldr为您的折叠步骤提供右侧值的“thunk”。这个“thunk”将在需要时计算。

让我们与foldr进行总结,看看它是如何评估的:

foldr (+) 0 [1..3]
1 + foldr (+) 0 [2..3]
1 + 2 + foldr (+) 0 [3]
1 + 2 + 3 + foldl (+) 0 [] -- this is a big thunk..
1 + 2 + 3 + 0
1 + 2 + 3
1 + 5
6

使用foldl' :(代码中省略了标记,因为SO不能很好地显示它)

foldl (+) 0 [1..3]
-- seq is a "strictness hint".
-- here it means that x is calculated before the foldl
x `seq` foldl (+) x [2..3] where x = 0+1
foldl (+) 1 [2..3]
x `seq` foldl (+) x [3] where x = 1+2
foldl (+) 3 [3]
x `seq` foldl (+) x [] where x = 3+3
foldl (+) 6 []
6

foldr的良好用途,不泄漏。 “步骤”必须:

  • 返回不依赖于“右侧”的结果,忽略它或将其包含在惰性结构中
  • 按原样返回右侧

良好foldr用法示例:

-- in map, the step returns the structure head
-- without evaluating the "right-side"
map f = foldr ((:) . f) []

filter f =
  foldr step []
  where
    step x rest
      | f x = x : rest -- returns structure head
      | otherwise = rest -- returns right-side as is

any f =
  foldr step False
  where
    -- can use "step x rest = f x || rest". it is the same.
    -- version below used for verbosity
    step x rest
      | f x = True -- ignore right-side
      | otherwise = rest -- returns right-side as is

答案 1 :(得分:4)

替换

foldr (\(n,a,b) (max,v) -> if n > max then (n , a * b) else (max ,v) ) (0,0) tuple_list

foldl' (\(max ,v) (n,a,b) -> if n > max then (n , a * b) else (max ,v) ) (0,0) tuple_list

解决了这个问题,如果这表明我们应该总是喜欢使用foldl'而不是其他变体(foldl,foldr)?