所以我开始学习Haskell了。在得到递归定义之后,我将阶乘定义编码为:
let fac n = if n==0 then 1 else n*fac(n-1)
(完全不同的编码方式,我知道:))
我认为这与python定义相同:
def fac(n):
if n==0:
return 1
else:
return n*fac(n-1)
我的问题是关于python引发的最大递归深度错误。尽管2个函数以相同的方式编码,但是当n = 1000时,是什么让python抛出错误并且计算结果?
答案 0 :(得分:2)
这不是尾递归,所以Haskell最终也会崩溃。
fac :: Int -> Int
fac n = if n == 0 then 1 else n*fac(n-1)
(答案不适合Int,只是让它更快地发生崩溃)
保留正确性问题,只需运行fac 10000000
并看到它因堆栈溢出而崩溃。
这是尾递归的:
fac :: Int -> Int
fac n = g 1 n where g a n = if n == 0 then a else g (a*n) (n-1)
不会崩溃。 (但也没有正确的答案,因为使用了Int)
(另外,正如在注释中正确指出的那样,如果我们将函数保留为默认的Integer -> Integer
类型,它将使用不受CPU体系结构约束的整数。但从那以后计算将花费更长的时间,我们需要更长的时间才能确保非尾递归最终崩溃。)
在此处的评论中,有g
在a
中有> ghc -O2 -ddump-simpl a.hs > a.dump.lazy
...
Rec {
Main.$wg [Occ=LoopBreaker]
:: GHC.Prim.Int# -> GHC.Prim.Int# -> GHC.Prim.Int#
[GblId, Arity=2, Caf=NoCafRefs, Str=DmdType LL]
Main.$wg =
\ (ww_s11J :: GHC.Prim.Int#) (ww1_s11N :: GHC.Prim.Int#) ->
case ww1_s11N of wild_Xn {
__DEFAULT ->
Main.$wg (GHC.Prim.*# ww_s11J wild_Xn) (GHC.Prim.-# wild_Xn 1);
0 -> ww_s11J
}
end Rec }
懒惰的投诉。虽然一般来说这是一个问题,但这不是重点,在这种特殊情况下没有区别:
g
现在,同样,但在a
中 fac :: Int -> Int
fac n = g 1 n where
g !a n = if n == 0 then a else g (a*n) (n-1)
> ghc -O2 -XBangPatterns -ddump-simpl a.hs > a.dump.eager
...
Rec {
Main.$wg [Occ=LoopBreaker]
:: GHC.Prim.Int# -> GHC.Prim.Int# -> GHC.Prim.Int#
[GblId, Arity=2, Caf=NoCafRefs, Str=DmdType LL]
Main.$wg =
\ (ww_s11P :: GHC.Prim.Int#) (ww1_s11T :: GHC.Prim.Int#) ->
case ww1_s11T of wild_Xs {
__DEFAULT ->
Main.$wg (GHC.Prim.*# ww_s11P wild_Xs) (GHC.Prim.-# wild_Xs 1);
0 -> ww_s11P
}
end Rec }
严格:
g
显然,优化程序可以看到a
的唯一返回值是a
,因此使{懒惰'{{1}}无效。