Haskell阶乘问题导致分段错误

6
GHCi, version 7.4.2: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> let fac 0 = 1
Prelude> let fac n = product [1..n]
Prelude> fac 100000
Segmentation fault: 11

有人知道为什么会出现这种情况吗?

fac 10000 可以工作。

运行在 OS X 10.8.2 上。

嗯,从文件中加载:

fac :: Integer -> Integer
fac 0 = 1
fac n = product [1..n]

运行。

有趣的是,使用

fac :: Int -> Int

对于 fac 100000 返回 0。我像 JohnL 一样期望出现错误。

这个网站 提到:

  • 更具体地说,SegmentationFault 是一种类型不安全的语言拼写 DoesNotUnderstand 的方式。在像 Haskell 这样的类型安全的静态类型语言中,你不应该看到 segfaults。

这和 IO monad 有关吗?


如果你要给反对票,请至少解释一下原因。 - beoliver
3
我不确定这里提供的信息已足够诊断问题。这明显属于“绝不应该发生”的范畴,因此你的系统出现了非常奇怪的问题。 - C. A. McCann
在这里,fac 100000 :: Integer 在 os x 上得到一个 456574 位数。而 fax 100000::Int 则是 0 :: Int,因为它有点粗略地说是 2^32(或 2^64)的倍数。 - applicative
这种例子很可能会导致堆栈溢出,但它绝不应该崩溃。 - MathematicalOrchid
1
可能是OSX上的一个bug,也可能是你的安装出了问题。这个程序不应该出现段错误,并且在我的电脑上可以正常运行(当然打印那个数字需要一些时间)。即使是fac 100000也不足以因为懒惰而导致堆栈溢出。 - Daniel Fischer
2个回答

2

经过快速测试,似乎是由于product不是严格的,并且thunk的链导致了故障。

在prelude中,product被定义为:

product = foldl (*) 1

如果在ghci中,你可以这样定义:

> :m + Data.List
> let product = foldl' (*) 1
> let fac n = product [1..n]

那么它应该可以工作了。我猜测当您指定类型签名时,可能会出现某些优化,否则不存在...但我还没有深入研究。

顺便说一下,您不需要let fac 0 = 1这行。


1
通过给它一个类型签名
fac :: Integer -> Integer 它就能工作了。虽然我不完全明白为什么。

如果您给出类型签名 fac :: Int -> Int,它是否有效?我怀疑它也会导致段错误。 - John L
1
@JohnL 使用 fac :: Int -> Int 实际上返回 0 对于 fac 100000 - beoliver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接