foldLeft在操作之前只需要集合中的一个元素。那么为什么要尝试解决其中两个呢?难道不是有点懒散吗?
def stream(i: Int): Stream[Int] =
if (i < 100) {
println("taking")
i #:: stream(i + 1)
} else Stream.empty
scala> stream(97).foldLeft(0) { case (acc, i) =>
println("using")
acc + i
}
taking
taking
using
taking
using
using
res0: Int = 294
我问这个是因为我在可变优先级队列周围构建了一个流,其中折叠的迭代可以将新成员注入流中。它从一个值开始,在第一次迭代期间注入更多值。但是从未见过其他值,因为在第一次迭代之前,流已经在位置2中被解析为empty
。
答案 0 :(得分:3)
只能解释为什么会这样。 Here是流#::
(Cons
)的来源:
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
}
}
因此,您可以看到始终计算head
(不是懒惰)。这是foldLeft
:
override final def foldLeft[B](z: B)(op: (B, A) => B): B = {
if (this.isEmpty) z
else tail.foldLeft(op(z, head))(op)
}
你可以看到这里调用tail
,这意味着“尾部头部”(第二个元素)会自动计算(因为它需要再次调用stream
函数来生成尾部) 。所以更好的问题不是“为什么第二” - 问题是为什么Stream
总是计算其第一个元素。我不知道答案,但相信scala-library的实现可以通过在lazy
内设置Cons
来改进,因此您可以通过someLazyCalculation #:: stream(i + 1)
。
请注意,您的stream
函数将被调用两次,但第二种方法为您提供了一种方法,通过提供一些惰性值作为头来避免自动第二个头的计算。像这样的Smthng可以工作(现在它没有):
def stream(i: Int): Stream[Int] =
if (i < 100) {
lazy val ii = {
println("taking")
i
}
ii #:: stream(i + 1)
} else Stream.empty
P.S。围绕可变的集合构建(最终)不可变集合可能不是一个好主意。