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