【问题标题】:OutOfMemoryError in a Fibonacci stream in ScalaScala中斐波那契流中的OutOfMemoryError
【发布时间】:2016-09-11 09:38:32
【问题描述】:

当我这样定义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)中的函数?

【问题讨论】:

    标签: scala functional-programming stream fibonacci


    【解决方案1】:

    在第一种情况下,在计算元素编号n 时,存在对fibs 流开头的引用——因此从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会再次出现。

    【讨论】:

    • 知道了。第二种情况没有问题,因为当我们从流中读取时,fibs 不再可见。第三种情况没有问题,因为fibs是一个返回流的函数,而不是对它的引用。对吗?
    • 是的。 // 填充以适应 SO 最小评论长度
    猜你喜欢
    • 2016-08-02
    • 2012-01-29
    • 1970-01-01
    • 2017-12-06
    • 2012-12-29
    • 1970-01-01
    • 2020-01-18
    • 2016-11-18
    • 2013-01-17
    相关资源
    最近更新 更多