这是欧拉项目问题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)
}
}
直观地我可以猜测伴随对象在初始化/拆除循环中被捕获,但是为什么会发生这种情况并不明显。
答案 0 :(得分:1)
程序将以val answer = ...
终止,但在此之前可能会耗尽资源。将1000000
更改为较小的数字,例如100
,以确定它实际终止。
原因是没有answer
而lazy 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。