パターンマッチングの威力は、昨日見たような型による分岐にとどまらない。というのも各パターンは(ケースクラスがパラメータを持つならば)パラメータを持つこともできるからだ。これでほぼ Haskell のパターンマッチングと同様のことができる。
例として、整数の四則演算を表す構文木を考えよう。各ノードは Exp を継承し(つまりすべては式である)、2項演算を表す部分木(2つの子を持つ)と整数リテラルを表す葉からなる。各クラスの定義はこうだ。
scala> sealed abstract class Exp defined class Exp scala> case class Add(lhs: Exp, rhs: Exp) extends Exp defined class Add scala> case class Sub(lhs: Exp, rhs: Exp) extends Exp defined class Sub scala> case class Mul(lhs: Exp, rhs: Exp) extends Exp defined class Mul scala> case class Div(lhs: Exp, rhs: Exp) extends Exp defined class Div scala> case class Lit(value: Int) extends Exp defined class Lit
これを組み合わせて、(1 + ((2 * 3) / 2)) という式の構文木を作るとこうなる。
scala> val example = Add(Lit(1), Div(Mul(Lit(2), Lit(3)), Lit(2))) example: Add = Add(Lit(1),Div(Mul(Lit(2),Lit(3)),Lit(2)))
さて、ここからが本番。この構文木を評価する評価器(メソッド)を作ろう。
scala> def eval(exp: Exp): Int = exp match { | case Add(l, r) => eval(l) + eval(r) | case Sub(l, r) => eval(l) - eval(r) | case Mul(l, r) => eval(l) * eval(r) | case Div(l, r) => eval(l) / eval(r) | case Lit(v) => v | } eval: (exp: Exp)Int
このパターンマッチングでは、
- ノードの種類と構造によって分岐し
- ネストしたノードを分解し
- 分解した結果を変数に束縛する
ということがいっぺんに行われている。もちろん束縛した変数は評価に使うことができる。
さて、それじゃ上の構文木 example を評価してみよう。
scala> eval(example) res2: Int = 4
ちゃんと (1 + ((2 * 3) / 2)) の計算結果である 4 が得られた。