Scala中的模式匹配如何在字节码级别实现?


123

Scala中的模式匹配如何在字节码级别实现?

它像一系列if (x instanceof Foo)结构,还是其他?它对性能有何影响?

例如,给定以下代码(来自Scala By Example第46-48页),该方法的等效Java代码将如何显示eval

abstract class Expr
case class Number(n: Int) extends Expr
case class Sum(e1: Expr, e2: Expr) extends Expr

def eval(e: Expr): Int = e match {
  case Number(x) => x
  case Sum(l, r) => eval(l) + eval(r)
}

PS我可以读取Java字节码,因此字节码表示形式对我来说已经足够了,但其他读者可能更清楚知道它看起来像Java代码一样好。

PPS《Scala中的编程》一书是否对此问题以及有关如何实现Scala的类似问题给出了答案?我已订购这本书,但尚未到货。


为什么不编译示例并使用Java字节码反汇编程序反汇编呢?
Zifre

除非有人先给出一个好的答案,否则我可能会这样做。但是现在我想睡一下。;)
Esko Luontola 09年

27
这个问题对其他读者很有用!
djondal

1
@djondal:最好的说法就是赞成这个问题:-)
Blaisorblade'2

Answers:


96

可以使用反汇编程序探索低级别,但简短的答案是,一堆if / elses谓词取决于模式

case Sum(l,r) // instance of check followed by fetching the two arguments and assigning to two variables l and r but see below about custom extractors 
case "hello" // equality check
case _ : Foo // instance of check
case x => // assignment to a fresh variable
case _ => // do nothing, this is the tail else on the if/else

模式之类的东西或诸如“ case Foo(45,x)”之类的模式和组合可以做的事情还很多,但是通常这些只是我刚才描述的逻辑扩展。模式也可以具有防护,这是对谓词的附加约束。在某些情况下,编译器可以优化模式匹配,例如,在两种情况之间存在某些重叠时,编译器可能会合并一些内容。高级模式和优化是编译器中的工作重点,因此,如果字节代码相对于当前和将来版本的Scala中的这些基本规则有了实质性的改进,请不要感到惊讶。

除此之外,除了Scala用于案例类的默认提取器之外,您还可以编写自己的自定义提取器。如果这样做,那么模式匹配的成本就是提取程序所做的任何事情的成本。在http://lamp.epfl.ch/~emir/write/MatchingObjectsWithPatterns-TR.pdf中可以找到很好的概述。



78

詹姆斯(上)说得最好。但是,如果您好奇的话,查看反汇编的字节码始终是一个很好的练习。您也可以scalac使用该-print选项进行调用,该选项将在删除所有Scala特定功能的情况下打印程序。它基本上是Scala服装中的Java。这是scalac -print您提供的代码段的相关输出:

def eval(e: Expr): Int = {
  <synthetic> val temp10: Expr = e;
  if (temp10.$isInstanceOf[Number]())
    temp10.$asInstanceOf[Number]().n()
  else
    if (temp10.$isInstanceOf[Sum]())
      {
        <synthetic> val temp13: Sum = temp10.$asInstanceOf[Sum]();
        Main.this.eval(temp13.e1()).+(Main.this.eval(temp13.e2()))
      }
    else
      throw new MatchError(temp10)
};

34

从2.8版开始,Scala具有@switch批注。目的是确保将模式匹配编译为tableswitch或lookupswitch而不是一系列条件if语句。


6
什么时候选择@switch定期转换?
Aravind Yarram

2
使用@switch比常规模式匹配更有效。因此,如果所有情况都包含常数值,则应始终使用@switch(因为字节码实现将与Java相同,switch而不是许多if-else)
lev
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.