scala视图过滤器不懒惰?

scala视图过滤器不懒惰?,scala,scala-collections,Scala,Scala Collections,在试图理解流、迭代器和集合视图之间的差异时,我偶然发现了以下奇怪的行为 这里的代码(映射和过滤器只需打印它们的输入并将其原封不动地转发): 其输出: stream-map-head: 1 stream-filter-head: 1 iterator-map-head: 1 iterator-filter-head: 1 view-map-head: 1 view-filter-head: 123456789 // <------ WHY ? 流图头:1 水流过滤头:1 迭代器映射头

在试图理解流、迭代器和集合视图之间的差异时,我偶然发现了以下奇怪的行为

这里的代码(映射和过滤器只需打印它们的输入并将其原封不动地转发):

其输出:

stream-map-head: 1
stream-filter-head: 1
iterator-map-head: 1
iterator-filter-head: 1
view-map-head: 1
view-filter-head: 123456789    // <------ WHY ?
流图头:1
水流过滤头:1
迭代器映射头:1
迭代器过滤器头:1
视图地图头:1

视图过滤器头:123456789/我认为必须这样做,
数组
是一个可变的索引序列。而且它的视图也是一个可变集合:),所以当它创建一个视图时,它会创建一个在原始集合和过滤集合之间映射的索引。懒散地创建这个索引是没有意义的,因为当有人请求第i个元素时,可能会遍历整个源数组。从某种意义上说,这个索引在调用
head
之前是不会创建的,这仍然是懒惰的。尽管如此,scala文档中并没有明确说明这一点,乍一看这似乎是一个bug


对于小问题,我认为迭代器上的
head
的问题在于人们期望
head
是纯函数,即您应该能够调用它n次,每次都应该返回相同的结果。迭代器本质上是可变的数据结构,按照约定只能遍历一次。这可以通过缓存迭代器的第一个元素来克服,但我发现这非常令人困惑。

似乎
头使用
isEmpty

trait IndexedSeqOptimized[+A, +Repr] extends Any with IndexedSeqLike[A, Repr] { self =>
...
override /*IterableLike*/
def head: A = if (isEmpty) super.head else this(0)
isEmpty
使用
length

trait IndexedSeqOptimized[+A, +Repr] extends Any with IndexedSeqLike[A, Repr] { self =>
  ...
  override /*IterableLike*/
  def isEmpty: Boolean = { length == 0 }
length
的实现是从
Filtered
中使用的,它在整个数组中循环

trait Filtered extends super.Filtered with Transformed[A] {
  protected[this] lazy val index = {
    var len = 0
    val arr = new Array[Int](self.length)
    for (i <- 0 until self.length)
      if (pred(self(i))) {
        arr(len) = i
        len += 1
      }
    arr take len
  }
  def length = index.length
  def apply(idx: Int) = self(index(idx))
}

这就是为什么在使用
过滤器
时会发生这种情况,而在使用
映射

时不会发生这种情况(迷你侧边答案)。如果您想获得迭代器的第一个元素,您必须使用
下一个
将其推进,并调用它(例如两次)将有问题。实际上,数组
视图
映射
函数中是懒惰的。原因是不需要在映射视图和源视图之间重建索引。另外,我不确定映射视图是否不是副本。。。非常困惑谢谢你的追踪!事实上,这个问题似乎只发生在
IndexedSeq
特性上(参见我编辑的帖子)。您是否同意这种实现方式是对性能的愚蠢浪费,可能会被称为bug?忽略任何内在的必要性:对我来说,通过单独计算数组的元素来计算数组的长度是完全没有必要的——事实上,O(1)访问数组的长度是数组的一个关键属性,事实上,关于阵列长度的信息就在那里——它不是由过滤器读取的。我同意性能并没有针对您的情况进行优化。我不知道这是一种折衷还是可以避免的。也许你可以要求澄清一下:Scala不是通过计算数组的元素来计算数组的长度。它通过计算视图的元素来计算视图的长度,但数组的视图不是数组。
trait Filtered extends super.Filtered with Transformed[A] {
  protected[this] lazy val index = {
    var len = 0
    val arr = new Array[Int](self.length)
    for (i <- 0 until self.length)
      if (pred(self(i))) {
        arr(len) = i
        len += 1
      }
    arr take len
  }
  def length = index.length
  def apply(idx: Int) = self(index(idx))
}
protected override def newFiltered(p: A => Boolean): Transformed[A] =
 new { val pred = p } with AbstractTransformed[A] with Filtered