作为构造函数arg的流有时在早期类初始化期间被完全评估

时间:2013-03-11 13:01:12

标签: scala stream initialization lazy-evaluation

Streams可以用作类构造函数参数:

scala> ( 0 to 10).toStream.map(i =>{println("bla" + i); -i})
bla0
res0: scala.collection.immutable.Stream[Int] = Stream(0, ?)

scala> class B(val a:Seq[Int]){println(a.tail.head)}
defined class B

scala> new B(res0)
bla1
-1
res1: B = B@fdb84e

因此,尽管作为Seq参数传递,但Stream并未得到完全评估,尽管部分评估。按预期工作。

我有一个这样的课程:

class HazelSimpleResultSet[T] (col: Seq[T], comparator:Comparator[T]) extends HGRandomAccessResult[T] with CountMe
{
  val foo: Int = -1  // col of type Stream[T] already fully evaluated here.

  def count = col.size
  ....
}

其中HGRandomAccessResult和CountMe是简单的接口。

在大多数情况下,我想使用Streams作为col构造函数参数,以避免代价高昂的操作。在调试器中我可以遵循它在某些情况下有效,因为即使在初始化HazelSimpleResultSet之后,col显示的值仍然是Stream(xy,?)和“tlVal = null”。

此外,为了进行测试,我将println包含在构成Streams的块中,如下所示:

    keyvalues.foldLeft(Stream.empty[KeyType]){ case (a, b) => ({ println("evaluating "+ b); unpack[KeyType](b)}) #:: a}

以便在确切评估Stream时在控制台中关注。

因此,在某些情况下它可以工作,但在某些情况下,在HazelSimpleResultSet初始化的最初时刻,Stream会得到完整的评估。我看不到Stream传递的相关差异,我只是确定他们是无价值的Streams直到那一刻。 使用调试器“进入”,我可以看到它在类定义本身的行中被评估,甚至在到达类体之前,即在任何字段的初始化之前。

修改: 我可以用(次优)方式定义类,这样根本就没有字段引用Stream,但我仍然得到了这种行为。

CountMe接口定义了一个“count”方法,该方法调用col.size然后评估所有Stream。我尝试用懒惰的val大小来定义计数,但这没有什么区别。

我有点不知道为什么它在某些情况下不起作用。任何人都有关于Streams隐藏警告的任何提示吗?

编辑: 一个重要的注意事项:Stream对象包含一些需要评估的严重状态,即对NoSQL数据库的引用(hazelcast)。 问题:这里有什么警告?当我的Stream带有评估所需的有状态引用时,我是否必须注意一些事项?

1 个答案:

答案 0 :(得分:1)

如果你这样创建Stream

Stream ({println("eval 1"); 1}, {println("eval 2"); 2})

然后你实际上正在调用Stream.apply,它实现如下:

/** A stream consisting of given elements */
override def apply[A](xs: A*): Stream[A] = xs.toStream

这意味着实际发生的是:

  1. 评估所有元素!
  2. 创建包含这些元素的Seq
  3. Stream
  4. 创建Seq

    正如您所看到的,如果您以这种方式创建Stream,则会急切地评估其所有元素。这不是你如何创建懒惰评估的Stream。您想要做的可能是使用#::#:::运算符来懒惰地评估它们的操作数。查看the docs的使用情况。