当我像这样定义fib
时(1):
def fib(n: Int) = {
lazy val fibs: Stream[BigInt] = 0 #:: 1 #:: fibs.zip(fibs.tail).map{n => n._1 + n._2}
fibs.drop(n).head
}
我收到错误:
scala> fib(1000000)
java.lang.OutOfMemoryError: Java heap space
另一方面,这很好用(2):
def fib = {
lazy val fibs: Stream[BigInt] = 0 #:: 1 #:: fibs.zip(fibs.tail).map{n => n._1 + n._2}
fibs
}
scala> fib.drop(1000000).head
res17: BigInt = 195328212...
此外,如果我按以下方式更改流定义,我可以在函数内调用drop(n).head
并且不会出现任何错误(3):
def fib(n: Int) = {
lazy val fibs: (BigInt, BigInt) => Stream[BigInt] = (a, b) => a #:: fibs(b, a+b)
fibs(0, 1).drop(n).head
}
scala> fib(1000000)
res18: BigInt = 195328212...
您能解释(1),(2)和(3)之间的相关差异吗?为什么(2)工作,而(1)不工作?为什么我们不需要将drop(n).head
移出(3)中的函数?
答案 0 :(得分:5)
在第一种情况下,对fibs
流的开头的引用存在,而元素号n
被计算 - 因此从0到1000000的所有值都必须保存在内存中。这是OutOfMemoryError
。
在第二种情况下,对流的开头的引用不会保留在任何地方,因此可以对项进行垃圾回收(一次只能将一个项保存在内存中)。
在第三种情况下,对流的开头的引用在任何地方都没有显式存在(它可以在删除下一个值时进行垃圾收集)。但是,如果我们将其更改为:
def fib(n: Int) = {
lazy val fibs: (BigInt, BigInt) => Stream[BigInt] = (a, b) => a #:: fibs(b, a+b)
val beg = fibs(0, 1)
beg.drop(n).head
}
然后再次发生OutOfMemoryError
。