为什么scala的集合默认不是"视图"?

dve*_*eim 6 collections scala

在某些情况下,在执行map/filter/...之前将'view'应用于集合会降低性能.然而,这些情况(afaik)很少(例如,当存在单个操作时).

但是大部分时间附加'.view'可以通过阻止创建中间集合来提高性能.

那么,为什么'view'默认不适用于集合?我错过了一些隐藏的费用吗?

Mic*_*jac 8

简短回答:视图并不总是有益的,默认情况下严格的行为可以防止意外.


考虑早期集合文档中的这个片段:

...你可能想知道为什么要有严格的收藏品?一个原因是性能比较并不总是偏向于严格的集合.对于较小的集合大小,在视图中形成和应用闭包的额外开销通常大于避免中间数据结构的增益.

考虑:

List(1, 2).map(_ + 1)
// vs
List(1, 2).view.map(_ + 1) 
Run Code Online (Sandbox Code Playgroud)

view使用一点点更多的开销,并与一些粗糙的测量速度慢约4%(体积小,但明显的区别).


文件继续说:

一个可能更重要的原因是,如果延迟操作具有副作用,则视图中的评估可能会非常混乱.

考虑:

for(i <- (1 to 10)) println(i)
Run Code Online (Sandbox Code Playgroud)

如果(1 to 10)实际上是view(事实并非如此),那么在被迫这样做之前不会发生任何事情.直截了当的代码可能不是.


更糟糕的是,您最终会遇到令人惊讶的情况,最终您需要重新评估您不需要的代码.拿这个天真的例子:

// Evaluates to List(2, 3, 4, 5), prints "eval" during each evaluation
val list = List(1, 2, 3, 4).view.map { i => println("eval"); i + 1 }

// For each element in the list, prints the elements that *not* that element (in a view)
def printList[A](list: Traversable[A]): Unit = 
    list foreach { i => list.filter(_ != i) }
Run Code Online (Sandbox Code Playgroud)

因为list是a view,比它应该printList评估list n - 1倍!

scala> printList(list)
eval
SeqViewMF(...)
eval
SeqViewMF(...)
eval
SeqViewMF(...)
eval
SeqViewMF(...)
Run Code Online (Sandbox Code Playgroud)

现在想象list没有明确标记为view(即默认情况下的视图).我可以想象许多奇怪的错误或瓶颈是由于意外的重新评估而发生的.

这并不是说严格的评估不会带来它自己的惊喜.这只是一个设计选择,严格的评估不像懒惰评估那样令人惊讶(Paul Phillips可能不同意).