编译器是否优化了链式映射?

Are chained maps optimized by compiler?

Scala 使用 map 构造将集合转换为另一个集合的惊人方法。

val l = List(1, 2, 3, 4)
l.map(_*_)

将return列表中元素的平方l

我遇到过多个地图链接在一起的各种实例,

val l = List(1, 2, 3, 4)
val res = l.map(_ * _).map(_ + 1).filter(_ < 3)

我认为下面发生的事情等同于下面的事情。

val l = List(1, 2, 3, 4)
val l1 = l.map(_*_)
val l2 = l1.map(_ + 1)
val res = l2.filter(_ < 3)

如果集合太大,创建 l1l2 可能会导致内存问题。 为了解决这个问题,Scala 编译器有什么优化吗?

val l = List(1, 2, 3, 4)
val res = l1.map( _*_ + 1).filter(_ < 3)

通常如果 fgh 是函数

val l = List(/*something*/)
val res = l.map(f(_)).map(g(_)).map(h(_))

可以转换成

val res = l.map(f _ andThen g _ andThen h _)

Scala 提供 Stream,这是一个惰性有序集合。

val s = Stream(1, 2, 3, 4)

// note i've changed your sequence of transformations
// a bit, so that it compiles and yields more than one result
val res = s.map(i => i * i).map(_ + 1).filter(_ < 11)

res 现在是 Stream。尚未执行实际评估,未使用与 s 大小相关的内存块。

如果您打算一次使用 res 中的一个元素,则无需再做任何工作。例如,您可以在 for 语句或理解中直接使用 res

for ( elem <- res ) println( s"A value is ${elem}" )

如果您希望 res 作为 List,您可以在转换序列的末尾调用 .toList。而不是上面的,使用

val res = s.map(i => i * i).map(_ + 1).filter(_ < 11).toList

s在创建新的List时只会遍历一次。

不,因为这需要编译器了解 map 的语义并对待实现它的标准库 classes(因为没有人阻止你写一个 class 这不成立)。有一个 research proposal 最终可能会实现这个...最终。

还有Scala-Blitz which optimizes some collection operations, but fusion and deforestation are listed as future work in this presentation,我认为它们还没有实现。

正如 Steve Waldman 的回答所说,使用 Stream(或者,更好的是 Iterator)可以提供帮助,但它不会完全消除中间集合。