scala 混合视图和严格集合用于表达

scala mixing view and strict collection in for expression

这段 scala 代码在 for 表达式中混合了视图和严格列表:

val list = List.range(1, 4)
def compute(n: Int) = {
  println("Computing "+n)
  n * 2
}

val view = for (n <- list.view; k<-List(1,2)) yield compute(n)
val x = view(0)

输出为:

Computing 1
Computing 1
Computing 2
Computing 2
Computing 3
Computing 3
Computing 1
Computing 1

我预计输出中应该只有最后两行 "Computing 1"。为什么它会急切地计算所有的值?为什么它又重新计算了这些值?

这里的视图是 SeqView[Int,Seq[_]],它是不可变的,并且在迭代时重新计算每个项目。

您可以通过显式使用 .iterator:

来访问第一个
@ view.iterator.next
Computing 1
Computing 1
res11: Int = 2

或者明确地把它变成一个列表(例如,如果你需要重复使用很多条目):

@ val view2: List[Int] = view.toList
Computing 1
Computing 1
Computing 2
Computing 2
Computing 3
Computing 3
view2: List[Int] = List(2, 2, 4, 4, 6, 6)

@ view2(0)
res13: Int = 2

可以说,按索引访问会强制计算视图。另外,请注意,您使用非惰性的东西平面映射 listk 不是视图)。

比较以下:

// 0) Your example
val v0 = List.range(1, 4).view.flatMap(n => List(1,2).map(k => compute(n)))
v0(0) // Computing 1 
      // Computing 1
      // Computing 2
      // Computing 2
      // Computing 3
      // Computing 3
      // Computing 1
      // Computing 1

v0(0) // Computing 1
      // Computing 1

// 1) Your example, but access by head and not by index
val v1 = List.range(1, 4).view.flatMap(n => List(1,2).map(k => compute(n)))
v1.head // Computing 1 
        // Computing 1

// 2) Do not mix views and strict lists
val v2 = List.range(1, 4).view.flatMap(n => List(1,2).view.map(k => compute(n)))
v2(0) // Computing 1

关于示例 0,注意视图不像流;虽然流会缓存它们的结果,但惰性视图不会(它们只是惰性地计算,即按需访问)。似乎索引访问需要计算整个列表,然后需要另一个计算才能真正按索引访问元素。

您可能会问为什么示例 2 中的索引访问不计算整个列表。这需要了解事物在底层是如何工作的;特别是,我们可以在以下摘录中看到示例 0 与示例 2 的方法调用的区别:

示例 0

java.lang.Exception scala.collection.SeqViewLike$FlatMapped.$anonfun$index(SeqViewLike.scala:75)
    at scala.collection.SeqViewLike$FlatMapped.index(SeqViewLike.scala:74)
    at scala.collection.SeqViewLike$FlatMapped.index$(SeqViewLike.scala:71)
    at scala.collection.SeqViewLike$$anon.index$lzycompute(SeqViewLike.scala:197)
    at scala.collection.SeqViewLike$$anon.index(SeqViewLike.scala:197)
    at scala.collection.SeqViewLike$FlatMapped.length(SeqViewLike.scala:84)
    at scala.collection.SeqViewLike$FlatMapped.length$(SeqViewLike.scala:84)
    at scala.collection.SeqViewLike$$anon.length(SeqViewLike.scala:197)
    at scala.collection.SeqViewLike$FlatMapped.apply(SeqViewLike.scala:86)
    at scala.collection.SeqViewLike$FlatMapped.apply$(SeqViewLike.scala:85)
    at scala.collection.SeqViewLike$$anon.apply(SeqViewLike.scala:197)
    at scala.collection.immutable.List.foreach(List.scala:389)
Computing 1

示例 2

java.lang.Exception scala.runtime.java8.JFunction1$mcII$sp.apply(JFunction1$mcII$sp.java:12)
    at scala.collection.SeqViewLike$Mapped.apply(SeqViewLike.scala:67)
    at scala.collection.SeqViewLike$Mapped.apply$(SeqViewLike.scala:67)
    at scala.collection.SeqViewLike$$anon.apply(SeqViewLike.scala:196)
    at scala.collection.SeqViewLike$FlatMapped.apply(SeqViewLike.scala:88)
    at scala.collection.SeqViewLike$FlatMapped.apply$(SeqViewLike.scala:85)
    at scala.collection.SeqViewLike$$anon.apply(SeqViewLike.scala:197)
    at scala.collection.immutable.List.foreach(List.scala:389)
Computing 1

特别是,您看到示例 0 导致调用 Flatmapped.length(需要评估整个列表)。