不使用lazy val时程序不会终止

时间:2012-12-06 18:17:17

标签: scala

这是欧拉项目问题14的解决方案。

但奇怪的是:当val回答未被声明为懒惰时,此程序不会终止。虽然val是懒惰的,但是解决方案在几秒钟内就会生成,而java vm很长时间内(可能是永久性的)会占用100%的CPU使用率。

package euler

object Problem14 {
//Which starting number, under one million, produces the longest chain?

    def startingNumbersFrom(n: Long): Stream[Long] = {
        if (n == 1) 
            Stream(1)
        else if (n % 2 == 0)
            n #:: startingNumbersFrom(n/2)
        else
            n #:: startingNumbersFrom(3*n+1)
    }

            //This has to be lazy for program to terminate
    lazy val answer = (
        for (n <- 1 to 1000000) yield 
        (n, startingNumbersFrom(n).length)
    ).maxBy(x=> x._2)._1


    def main(args: Array[String]) = {
        println(answer)
    }
}

直观地我可以猜测伴随对象在初始化/拆除循环中被捕获,但是为什么会发生这种情况并不明显。

2 个答案:

答案 0 :(得分:1)

程序将以val answer = ...终止,但在此之前可能会耗尽资源。将1000000更改为较小的数字,例如100,以确定它实际终止。

原因是没有answerlazy val不起作用是因为它的评估方式。对length的{​​{1}}调用迫使其评估为某个值。当startingFromNumbers严格时,val answers的整个计算在每次迭代时发生。对于像100这样的小值,这种低效率会被忽视,但是在较大的值下,每次迭代时重新计算似乎永远都会运行。使用startingFromNumbers会记住结果lazy val answer以避免重复计算。所以,正如你所发现的那样,Ryan的回答使他们既懒惰又使他们都严格。

减少问题有助于在startingFromNumbers中第一次调用时添加println(n),有助于形象化。

startingFromNumbers

在第一个例子中,我们可以看到在每次迭代时计算所有值。在第二个例子中,只计算第一个值,其余的被延迟直到需要,即延迟。如果使用scala> val answer = (for (n <- 1 to 3) yield (n, startingNumbersFrom(n).length)) 1 2 1 3 10 5 16 8 4 2 1 answer: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,1), (2,2), (3,8)) scala> val answer = (for (n <- 1 to 3) yield (n, startingNumbersFrom(n))) 1 2 3 answer: scala.collection.immutable.IndexedSeq[(Int, Stream[Long])] = Vector((1,Stream(1, ?)), (2,Stream(2, ?)), (3,Stream(3, ?))) 评估answer(i)._2,我们可以看到与第一个示例中相同的数字。

答案 1 :(得分:0)

我能够在Scala 2.9.2上重现问题。但是,如果我将代码更改为使用List [Int]而不是Stream [Int],则非延迟版本根本不会固定CPU。