2012年9月20日木曜日

[Scala] 一般クラスのマッチ

List や Tuple ではない一般のクラスの場合、unapply、unapplySeq いわゆる抽出子(extractor)を定義しておくことで match ... case 文で使えるようになる。

...
  val a = new MatchSampleClass;
  a match {
    case MatchSampleClass(x, y) => 
      println("x: " + x + " y: " + y);
    case _ => println("OTHER: " + a);
  }
....

class MatchSampleClass
object MatchSampleClass {
  def unapply(obj : MatchSampleClass) : Option[(String, String)] = {
    return Option(("ONE", "TWO"));
  }
}

x: ONE y: TWO

unpapply は Tupple を Some オブジェクトでラップして返す。
case 句では「MatchSampleClass(x, y)」と MatchSampleClass.apply が呼ばれそうな構文になっているが、 実際には unapply が呼ばれるだけで、apply メソッドが定義されている必要はない。

unapply の代わりに unapplySeq を定義してもよい。
unapplySeq では Tuple の代わりに Seq オブジェクトを Some オブジェクトでラップして返す。

...
  val a = new MatchSampleClass;
  a match {
    case MatchSampleClass(x, y) => 
      println("x: " + x + " y: " + y);
    case _ => println("OTHER: " + a);
  }
....

class MatchSampleClass
object MatchSampleClass {
  def unapplySeq(obj : MatchSampleClass) : Option[List[String]] = {
    return Option(List("ONE","TWO"));
  }
}

x: ONE y: TWO



unapply と unapplySeq の使われ方


クラスが unapply と unapplySeq が定義されている場合は unapply が使われる。
unapply では抽出されるオブジェクト数が固定なので以下のようなコードはコンパイル時にエラーになる。

...
  val a = new MatchSampleClass;
  a match {
    case MatchSampleClass(x, "AAA", z) =>
      println(" x: " + x);
    case _ => println("OTHER: " + a);
  }
...
class MatchSampleClass
object MatchSampleClass {
  def unapplySeq(obj : MatchSampleClass) : Option[List[String]] = {
    return Option(List("ONE", "TWO"));
  }
  def unapply(obj : MatchSampleClass) : Option[(String, String)] = {
    return Option(("ONE", "TWO"));
  }
}
wrong number of arguments for object MatchSampleClass

このコードで unapply の定義を削除すると可変長の unapplySeq が使われるのでエラーにはならない。上のコードの場合はビルドエラーにはならないがマッチもしないので、case _ の節が実行される。

実際に unapply や unapplySeq が実行されるのは1回だけ。各case節ごとに実行されるわけではない。

...  val a = new MatchSampleClass;
  a match {
    case MatchSampleClass(x, "AAA") =>
      println(" x: " + x);
    case MatchSampleClass(x,y) =>
      println("x: " + x + " y: " + y);
    case _ => println("OTHER: " + a);
  }
...
class MatchSampleClass
object MatchSampleClass {
  def unapply(obj : MatchSampleClass) : Option[(String, String)] = {
    println("unapply Called")
    return Option(("ONE", "TWO"));
  }
}

unapply Called x: ONE y: TWO

最初のcase 句にはマッチしないため2番目の case 句でマッチしているが、 unapply 自体は1回しか実行されていない。 最初に引数リストに分解し、それが各 case 句にマッチするかを調べるような作りになっているようだ。



0 件のコメント:

コメントを投稿