Scalaのパターンマッチングはバイトコードレベルでどのように実装されますか?


123

Scalaのパターンマッチングはバイトコードレベルでどのように実装されますか?

それは一連のif (x instanceof Foo)構成要素のようなものですか、それとも何か他のものですか?そのパフォーマンスへの影響は何ですか?

たとえば、次のコード(Scala By Exampleページ46〜48から)を指定すると、evalメソッドの同等のJavaコードはどのようになりますか?

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 2009

誰かが最初に良い答えをしない限り、私はおそらくそれをします。でも今は少し眠りたいです。;)
Esko Luontola

27
質問は他の読者に役立ちます!
djondal

1
@djondal:それを言う最善の方法は質問を
賛成

回答:


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/written/MatchingObjectsWithPatterns-TR.pdfにあります


私は、これは現在のリンクであると信じて:infoscience.epfl.ch/record/98468/files/...
greenoldman

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
他の場合、定期的に@スイッチを選択するタイミング?
Aravind Yarram 16

2
を使用する@switchと、通常のパターンマッチングよりも効率的です。したがって、すべてのケースに定数値が含まれている場合は、常に使用する必要があります@switch(バイトコードの実装はswitch多くのif-elseではなくjavaと同じであるため)
lev
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.