为什么Haskell中的递归函数如此之慢?

时间:2011-08-21 05:53:15

标签: haskell

我试图模仿Sieve使用Haskell找到所有素数少于一些数。我发现其他Haskell程序以极快的速度使用Sieve方法。但是我写的下面的递归函数非常慢。代码如下

sieve' :: Integer -> Integer -> [Integer]

sieve' n 1 = [2 .. n]

sieve' n (k + 1) | [x | x <- sieve' n k, x == k + 1] == [] = sieve' n k

    |otherwise = [x | x <- sieve' n k,  x == k + 1 || not (mod x (k + 1) == 0)]



sieve :: Integer -> [Integer]

sieve n = sieve' n n

Sieve 20大约需要2分钟。在我写这个问题的时候,Sieve 30还没有完成。

任何人都可以解释为什么这个递归函数太慢了。感谢您的任何帮助,您可以提供。

1 个答案:

答案 0 :(得分:15)

sieve'函数的第二个子句正在进行两次递归调用(sieve' n k),从而使您的算法在指数时间内执行。

要解决此问题,您可以将该术语绑定到某个名称,从而确保对其进行一次评估:

sieve' n (k + 1) | [x | x <- rec, x == k + 1] == [] = rec
    |otherwise = [x | x <- rec,  x == k + 1 || not (mod x (k + 1) == 0)]
  where
    rec = sieve' n k

更新以回应评论,询问编译器为何不自动执行此操作:

这种称为CSE(公共子表达式消除)的转换一般不是优化,而是时间和空间使用之间的权衡,因此最好留给程序员做出决定。

谷歌搜索“CSE”揭示了一些有趣的讨论,其中一个引用了1987年Simon Peyton Jones所着的this very good example,称为“函数式编程语言的实现”(哦,我的,这本书几乎和我一样久是)