Learn You a Haskell显示factorial
功能:
Prelude> factorial n = product [1..n]
Prelude> factorial 50
30414093201713378043612608166064768844377641568960512000000000000
看起来Int
大于32位,或者Haskell有类似BigNumber
的类型。
运行"类似" Scala中的函数。
scala> def factorial(n: Int) = List.range(1, n+1).foldLeft(1)(_*_)
factorial: (n: Int)Int
我可以计算出一个保持在Int.MaxValue
(20亿东西)
scala> factorial(10)
res5: Int = 362880
由于结果超出Int.MaxValue
scala> factorial(33)
res6: Int = -2147483648
但是,为什么它不会溢出来?
scala> factorial(50)
res7: Int = 0
那么,它在Haskell中是如何运作的?而且,为什么它会导致Scala为0?
答案 0 :(得分:9)
Integral的标准实例是Integer(无界或无限制) 数学整数,也称为" bignums")和Int(有界, 机器整数,其范围相当于至少29位有符号 二进制)
Wikibooks解释了这一点:
"整数"是一个任意精度类型:它将保留任何数字号 无论机器的记忆有多大,都达到了极限......这意味着 你永远不会有算术溢出。另一方面,它也意味着 你的算术比较慢。 Lisp用户可能会认识到 " BIGNUM"在这里输入。
在ghci:
Prelude> factorial 50 :: Int
-3258495067890909184
Prelude> factorial 50 :: Integer
30414093201713378043612608166064768844377641568960512000000000000
所以,Int
它实际上溢出了。 Integer
可以保留任何数字而不受系统内存限制的溢出。
答案 1 :(得分:7)
Haskell中存在(至少)两种类型的整数:Integer
可以容纳任意大数,但算术速度较慢,Int
,具有机器相关的最大值和更快的算术(另见这个问题:Haskell Int and Integer)
Haskell的默认整数类型是Integer
,这就是为什么你的haskell示例中没有溢出的原因。然而,Scala的Int
(32位签名)可以溢出然后换成负数。
这就是factorial(50)=0
(和factorial(51) = factorial(52) = ... = 0
中出现奇数结果的原因:由于溢出,乘法中的临时结果为0
,因此所有后续因子都是0
也是如此。
使用十进制数字的示例: 让我们假设我们使用固定宽度的十进制表示,有3个位置而且只有正数(实际上,我们将有32或64位二进制数字,而Haskell和Scala都使用有符号数字,但基本概念保持不变):
我的64位盒子上的一个例子,使用Haskell:
Prelude> factorial 65 :: Int # largest nonzero factorial on my box
-9223372036854775808
Prelude> factorial 66 :: Int # all following factorials are zero
0
Prelude> -9223372036854775808 * 66 :: Int # because (factorial 65) * 66 = 0
0
Prelude> -9223372036854775808 * 66 # with arbitrary precision:
-608742554432415203328
Prelude> -608742554432415203328 / 2^64 # this happens to be a multiple of 2^64
-33.0
同样的事情会发生在固定长度基数10表示中的阶乘:每个因子是10的倍数在数字"的右端引入另一个0,所以最终,所有数字我们想象的固定长度基数10整数向左移出。
使用Haskells或Scalas Int
中使用的内部二进制表示,每个偶数因子都会发生相同的情况,因此在某些时候,所有位都为0。
答案 2 :(得分:6)
除了上述Haskell Int
和Integer
机器相关和任意精度的原因之外,Scala中Integer
的等价物将是
def factorial(n: Int) = (BigInt(1) to BigInt(n)).product
其中BigInt
提供任意精度并依赖于java.math.BigInteger
。