【问题标题】:Scala For-Loop / For-Yield Optimization?Scala For-Loop / For-Yield 优化?
【发布时间】:2015-06-15 21:10:19
【问题描述】:

在 Scala 中,forfor-yield 循环通常被转换为使用 Lambda 调用的 mapflatMapfilter 的序列。由于编译器将那里的 Lambda 转换为匿名类,而不是 Java 8 的花哨的 invokedynamic / LambdaMetafactory / Unsafe.defineAnonymousClass 系统。这显然会为临时类及其实例带来大量开销,以及通常复制其底层集合的 map、flatMap 和过滤器操作的开销。当通常更快的迭代方法可用时,至少在字节码级别上,是否有任何特别的理由使用函数式方法来解决问题?

例如,为什么

for {
  sl <- l
  el <- sl
  if el > 0
} println el.toString.length

翻译成

l.flatMap(sl => sl.filter(el => el > 0).foreach(el => println el.toString.length))

代替

for (sl <- l) // Iterable for loop
{
    for (el <- sl) // Iterable for loop
    {
        println el.toString.length
    }
}

【问题讨论】:

  • filter 通常有withFilter - 编译器在必须回退到过滤器时会生成警告,我相信 2.11 或 2.12 完全拒绝 if 保护未实现 @ 的类987654336@

标签: scala lambda for-comprehension


【解决方案1】:

之所以这样实现,是因为 SLS 是这样定义它的。这些规则相当大,无法粘贴到此处,但它们已完全包含在规范中。

For Comprehensions and For Loops

示例 以下代码生成 1 到 n−1 个和为素数。

for  { i <- 1 until n
       j <- 1 until i
       if isPrime(i+j)
} yield (i, j)

for 推导式翻译为:

(1 until n)
  .flatMap {
     case i => (1 until i)
       .withFilter { j => isPrime(i+j) }
       .map { case j => (i, j) } }

【讨论】:

  • 我明白了。但是除了规范之外,是否存在简单的for 循环not 可以转换为迭代版本的情况? (由于yield 返回一个集合,所以无论如何它都不起作用)
  • @Clashsoft 您必须研究 1) scalac 如何将 AST 优化为 jvm 字节码,然后 2) jvm 如何优化该字节码。
  • Scala 编译器不优化这种特殊情况的原因是 Scala 编译器开发人员不喜欢特殊情况。所以,这个特殊情况并没有什么特别的,从某种意义上说,no 特殊情况是由编译器优化的。编译器开发人员更喜欢实现对大量代码有益的强大的通用优化,而不是在编译器中乱扔只有益于特定极端情况的微优化。例如,让一流的函数更快,将在更广泛的环境中受益。
  • 而且,事实上,从Scala 2.12 开始,运行 Scala 程序的最低 Java 平台版本将被提升到 Java 8,这正是因为一流函数的处理方式与 Java 8 的处理方式类似它:“Scala 2.12 计划进行以下更改:Java 8 样式闭包。Scala 编译器将以与 Java 8 相同的方式发出闭包类 (lambdas)。”同样相关:“新的后端和优化器。[…] Scala 2.12 还将附带一个新的内联和字节码优化器。”
猜你喜欢
  • 1970-01-01
  • 2014-05-10
  • 1970-01-01
  • 2012-06-27
  • 2017-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多