Scala中的因子和哈斯克尔

时间:2014-04-05 16:29:18

标签: scala haskell

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?

3 个答案:

答案 0 :(得分:9)

根据documentation

  

Integral的标准实例是Integer(无界或无限制)   数学整数,也称为" bignums")和Int(有界,   机器整数,其范围相当于至少29位有符号   二进制)

Wikibooks解释了这一点:

  

"整数"是一个任意精度类型:它将保留任何数字号   无论机器的记忆有多大,都达到了极限......这意味着   你永远不会有算术溢出。另一方面,它也意味着   你的算术比较慢。 Lisp用户可能会认识到   " BIGNUM&​​#34;在这里输入。

在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都使用有符号数字,但基本概念保持不变):

  • factorial 1 = 001
  • factorial 2 = 002
  • ...
  • factorial 5 = 120
  • 阶乘6 = 720
  • factorial 7 = 5040,但我们只能存储3位数,所以这会被截断为040
  • 阶乘8 =(阶乘7)* 8 = 040 * 8 = 320
  • 阶乘9 = 320 * 9 = 2880 = 880
  • 阶乘10 = 880 * 10 = 8800 = 800
  • 阶乘11 = 800 * 11 = 8800 = 800
  • 阶乘12 = 800 * 12 = 9600 = 600
  • ...
  • 阶乘15 = 200 * 15 = 3000 = 000
  • 阶乘16 =(阶乘15)* 16 = 000 * 16 = 000
  • ...

我的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 IntInteger机器相关和任意精度的原因之外,Scala中Integer的等价物将是

def factorial(n: Int) = (BigInt(1) to BigInt(n)).product

其中BigInt提供任意精度并依赖于java.math.BigInteger