2013-05-01 63 views
1

我正在学习scala并享受它,它是一种非常强大的语言。我写了这个程序来解决Eu​​ler#2问题,来自项目欧拉。它可以找到甚至斐波纳契数字< 400万(称为最大#)的总和。Stream上的scala过滤器产生错误的结果

首先我用takeWhile,然后过滤:

fib().takeWhile(_ < n).filter(_ % 2 == 0).sum 

然后决定takeWhile之前更改顺序,并使用过滤器:

fib().filter(_ % 2 == 0).takeWhile(_ < n).sum 

我这样做是为了检查是否有性能差异当增加最大#时。如预期的那样,结果是准确的,并且性能几乎相同,直到我使用此输入数字(40000000000000)并得到2个不同的结果。结果是:

$ scala com.ms.E2_1 40000000000000

    takeWhile 1st => 3770056902373173214 
    filter 1st => -8573983172444283806 

有人能解释为什么我在使用filter之前得到这个错误吗?谢谢。

object E2_1 { 

def fib(a: Long = 0, b: Long = 1): Stream[Long] = { 
    a #:: fib(b, a + b) 
} 

def main(args: Array[String]): Unit = { 
    if (args.length == 1) { 
    val n = args(0).toLong  
    println("takeWhile 1st \t=> " + fib().takeWhile(_ < n).filter(_ % 2 == 0).sum)  
    println("filter 1st \t=> " + fib().filter(_ % 2 == 0).takeWhile(_ < n).sum) 
    } else { 
    println("missing args") 
    } 
} 
} 
+0

看起来你刚刚溢出。在数学上,你的'fib'可能会单调递增,但实际上你使用的是两个补码“Long”,一旦它们变得足够大,它们就会翻牌。或者如果不是流值本身,那么它们的总和。 – copumpkin 2013-05-01 04:43:23

+0

非常感谢copumpkin,但为什么在使用过滤器之前使用过滤器而不是其他方式溢出? – Mark 2013-05-01 04:54:02

回答

2

为什么溢出影响行为的原因不同,具体取决于功能的顺序是这样的:

scala> fib().takeWhile(_ > -1).toList.reverse.take(2) 
res0: List[Long] = List(7540113804746346429, 4660046610375530309) 

即计算溢出开始前的最后一个数字,这是不是你的目标n更大,是奇数

当您首先应用takeWhile时,生成的流将生成这些数字中的一个。该数字大于n,因此在结果达到溢出之前停止生产。

当您首先应用filter时,这些奇数将被丢弃。由于溢出,产生的下一个偶数可能是负数,因此小于n,所以它通过takeWhile中的谓词!该流将继续,直到结果再次变为正值,并且产生大于n的数字。

+0

谢谢你,这是有道理的,我试了下来,并修复了我的过滤器。 (print()(filter(x))(println(fib()filter(_%2 == 0)takeWhile(_ x%2 == 0 && x> -1)takeWhile(_ Mark 2013-05-02 02:57:47

+0

它不一定是无限的。它可能会停止,因为它会重新溢出并成为大于目标'n'的正数。 – 2013-05-02 07:23:18