我正在通过真实世界Haskell(我在第4章)中工作,并且练习了一下我创建了以下程序来计算第n个素数。当计算素数时堆栈空间溢出
import System.Environment
isPrime primes test = loop primes test
where
loop (p:primes) test
| test `mod` p == 0 = False
| p * p > test = True
| otherwise = loop primes test
primes = [2, 3] ++ loop [2, 3] 5
where
loop primes test
| isPrime primes test = test:(loop primes' test')
| otherwise = test' `seq` (loop primes test')
where
test' = test + 2
primes' = primes ++ [test]
main :: IO()
main = do
args <- getArgs
print(last (take (read (head args) :: Int) primes))
很明显,既然我保存了素数列表,这不是一个恒定的空间解决方案。问题是,当我试图得到一个非常大的素说./primes 1000000
我收到的错误:
Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize -RTS' to increase
我相当肯定,我得到了尾递归权利;阅读http://www.haskell.org/haskellwiki/Stack_overflow,这里的各种回应让我相信它是懒惰评价的副产品,而且它正在积累,直到它溢出,但到目前为止,我一直未能解决它。我试过在各个地方使用seq
来强制评估,但没有产生效果。我在正确的轨道上吗?还有什么我没有得到?
你说得对,懒惰与尾递归不能很好地融合,但是当使用列表时,解决方案通常是去除尾递归,而不是去除懒惰。 – sepp2k 2012-08-12 22:44:42
@ sepp2k虽然它通常不是彻底删除TR,而是将它变成“尾递归模保留”(不仅仅是* list * cons,* any * cons),也就是守护递归。但要说TRMC根本不是TR,不会觉得IMO是正确的。也许这个概念不如它应该是的。 :)当守卫rec不是TRMC,那正好意味着thunk堆积起来。 TRMC意味着,*常数*递归调用之间的工作量,重新安排*在调用之前完成,而不是*在*之后,如在常规递归方案中那样。 – 2012-08-13 06:53:25