0

这是一个关于 Scala 编译器的问题。

假设我有一个列表,并且我通过几个地图和平面地图来转换该列表。

val myList = List(1,2,3,4,5)

val transformed = myList.map(_+1).map(_*2).flatmap(x=>List(x-1,x,x+1))

假设我再对其进行一些改造。

val moreTransformed = transformed.map(_-2).map(_/5).flatMap(x=>List(x/2,x*2))

我的问题可以分为两部分

  1. 为 生成 val 时transformed,底层 Java 字节码是否创建中间列表?我指的是在计算中对 map 和 flatMap 的连续调用transformed。Scala 编译器可以将这些调用组合成一个 flatMap 吗?如果我在对象列表上进行操作,这将需要创建更少的中间对象。如果编译器是幼稚的并且只是创建中间对象,那么这可能会导致涉及长链 map 和 flatMap 的计算的相当大的开销。

  2. 假设在上面创建的两个 val 中,我仅moreTransformed在进一步计算中使用(第二个 val)。也就是说,我只transformed在计算中使用(第一个val)moreTransformed而没有其他地方。Scala 编译器是否足够聪明,不会transformed只为和计算创建列表moreTransformedtransformed将所有函数组合在and中是否足够聪明,moreTransformed从而只生成一个 List,即 的值moreTransformed

4

2 回答 2

2

我不确定,编译器生成什么样的字节码。我将尝试从概念上回答它

如果我在对象列表上进行操作,这将需要创建更少的中间对象。如果编译器是幼稚的并且只是创建中间对象,则可能会导致涉及长链 map 和 flatMap 的计算的相当大的开销。

是的。Scala 的集合List是默认的strict,这意味着所有需要的中间对象都将被计算和生成。

Scala 编译器是否足够聪明,不会为转换创建列表并仅计算 moreTransformed?

简短的回答,不

将所有的函数组合在transformed 和moreTransformed 中是否足够聪明,从而只产生一个List,即moreTransformed 的值?

如果您想要第 2 和第 3 块中提到的功能,还有LazyListAPI Stream。一般来说,惰性集合对于描述连续的转换操作而不评估中间转换特别有用。

您可以在此处strict阅读有关和lazy评估的简要概述。

在这里做一些练习lazy evaluation

于 2019-08-17T05:30:26.740 回答
2

不管编译器有多聪明,它仍然必须符合语言指定的内容

这种情况下,语言表明每个map/flatMap操作必须在下一个操作开始之前完成。因此,如果编译器可以保证行为相同,则编译器只能执行您提到的优化。

在问题中示例的具体情况下,编译器知道 in 是什么myList,并且应用的函数具有非常清晰的语义。编译器理论上可以对此进行优化并预先计算结果,而无需在运行时执行任何操作。

在更一般的情况下,编译器将不知道里面有什么myList,操作将有可能失败。在这种情况下,编译器别无选择,只能依次执行每个操作。这是根据语言保证正确结果的唯一方法。


请注意,Scala 代码通常在带有 JIT 编译器的 JVM 中执行,而大部分优化都是在此处完成的。顺序map调用将被转换为字节码中的顺序循环,在某些情况下,JIT 编译器可能能够将这些循环组合成一个循环。但是,如果循环中有任何副作用(包括对象分配),则无法进行此优化。

于 2019-08-17T06:07:37.953 回答