Scala:跨转换的Stream和List的内存使用情况

时间:2016-09-25 09:52:53

标签: scala

我怀疑Scala如何使用StreamList在转换中分配内存。我们来看一个使用Stream/List

编写的简单示例
Stream(1,2,3,4,5).map(_ + 3).filter (_ % 2 ==0).toList

List(1,2,3,4,5).map(_ + 3).filter (_ % 2 ==0)

使用List(1,2,3,4,5)将在内存中首先分配一个新的“结构”(让我们说另一个List)在过滤后的map然后另一个List之后总共2 List s,加上每个列表中包含的元素分配了什么?

如果Stream只有一个吗?

如果两者都是“是”,为什么Scala不使用Stream行为作为转换的默认行为?

1 个答案:

答案 0 :(得分:2)

懒惰的评估很难说明何时评估事物并且懒惰不能很好地应用副作用

  

懒惰列表(通常是懒惰数据结构)的内存消耗很难处理,因为它们会导致内存中未评估的thunk堆积。

     

懒惰不适用于副作用。在副作用的情况下,何时在计算中发生副作用。由于推迟评估推迟,副作用的时间很难推理。   例如,读取/写入全局变量时很重要。当发生数据库写入/读取以及打印到控制台时,这很重要

Thunk表示要评估的延迟计算或未评估的表达式。

延迟流(或延迟列表)会导致占用内存的thunks(计算描述)。除非明确要求,否则不会评估这些thunk。在程序执行过程中,由于内存中未评估的thunk积累,内存消耗可能会非常高。这也是haskell列表的经典问题。默认情况下haskell是懒惰的。这就是为什么有一个严格的折叠版本和一个懒惰的折叠版本来解决这类问题。

让我们来看看Stream代码

会发生什么

以下是标准lib

的过滤器代码
override def filter(p: A => Boolean): Stream[A] = {
    // optimization: drop leading prefix of elems for which f returns false
    // var rest = this dropWhile (!p(_)) - forget DRY principle - GC can't collect otherwise
    var rest = this
    while (!rest.isEmpty && !p(rest.head)) rest = rest.tail
    // private utility func to avoid `this` on stack (would be needed for the lazy arg)
    if (rest.nonEmpty) Stream.filteredTail(rest, p)
    else Stream.Empty
  }

private[immutable] def filteredTail[A](stream: Stream[A], p: A => Boolean) = {
    cons(stream.head, stream.tail filter p)
  }

这是懒惰评估的thunk tl

/** A lazy cons cell, from which streams are built. */
  @SerialVersionUID(-602202424901551803L)
  final class Cons[+A](hd: A, tl: => Stream[A]) extends Stream[A] {
    override def isEmpty = false
    override def head = hd
    @volatile private[this] var tlVal: Stream[A] = _
    @volatile private[this] var tlGen = tl _
    def tailDefined: Boolean = tlGen eq null
    override def tail: Stream[A] = {
      if (!tailDefined)
        synchronized {
          if (!tailDefined) {
            tlVal = tlGen()
            tlGen = null
          }
        }

      tlVal
    }
  }

tl是遗留在内存中的块,直到触发其评估。 Thunk也可能会产生多个其他thunk,这些thunk可能会因记忆而无法评估。

Stream(1, 2, 3, 4, 5, 6).filter(_ % 2 == 0)

cons(2, thunk)

cons(2, cons(4, thunk)

cons(2, 4, 6, thunk)