2009-08-18 88 views
4

运行下面的程序会打印出“空间溢出:当前大小8388608字节”。我已阅读thisthis,但仍不知道如何解决我的问题。我正在使用foldr,它不应该保证是“尾递归”吗?如何解决haskell中的“堆栈空间溢出”

直到我知道我应该在使用强大的递归时应该防止“空间溢出”时,我对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的definiton为简单起见

+4

这是很难用任何功能的语言。作为一个聪明的家伙,我知道曾经说每个抽象都是漏洞。函数式语言非常适合表达自己,并展示算法将正确完成,但他们都必须假设它们具有无限的记忆。 欢迎来到适合你的美丽程序到真正的电脑世界... – Spence 2009-08-18 07:32:37

回答

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 
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)?

+6

看到这篇文章:http://www.haskell.org/haskellwiki/Foldr_Foldl_Foldl%27 – fishlips 2009-08-18 07:32:36