ページ

2012年9月23日日曜日

[Scala] match 文の仕組み

このブログを見て match 文 の仕組みが分かった気がするのでメモしておく。
List のマッチでは case List(x,y) みたいな形式だったので気が付かなかったのだが、 case 句には オブジェクトも書くことができる。

case List(x,y) 形式ではマッチのために List.unapply が呼ばれていたのだが、 case オブジェクト 形式ではメンバーメソッドの unapply が実行される。
unapply 実行時には match 句の前に書いてあるオブジェクトが渡されるので、 これを使えば 独自クラスの マッチでいろいろなことができる。
  val values = List("abc", "cde");
  val head = new Head(1);
  for (value <- values) 
    value match {
      case head("a") => println(value + " は a で始まります。");
      case head(x) => println(
           value + " は a ではなく " + x + " で始まります。");
    }
...
private class Head(count : Int) {
  def unapply(value : String) : Option[String] = 
     Some(value.substring(0, count));
}

----------------------
abc は a で始まります。
cde は a ではなく c で始まります。
Head#unapply は 引数に文字列を受け取り、コンストラクタで指定されている長さをその文字列から切り出して返す。もはや抽出子でも何でもないが Scala はそんなことは気にせず呼び出してくれるのでこのような使い方ができる。
上の例では head オブジェクトの unapply が value を引数として実行され、その結果が "a" と比較されたり、xに代入されたりしている。
  val head = new Head(1);
  "cde" match {
    case head("a") => println("cde は a で始まります。");
    case head(x) => println("cde は a ではなく " + x + " で始まります。");
  }
... 
private class Head(count : Int) {
  def unapply(value : String) : Option[String] = {
     println("*** unapply called");
    Some(value.substring(0, count));
  }
}

-----------------------
*** unapply called
cde は a ではなく c で始まります。
この出力を見ると、2つ目の case にマッチしているが unapply は1度しか実行されていないことがわかる。 少なくとも各 case 句に出現するオブジェクトが同じであれば unapply は1度しか実行されない。

ならば違うオブジェクトが出現している場合はどうなるだろうか?
  val head1 = new Head(1);
  val head2 = new Head(2);
  "cde" match {
    case head1("a") => println("cde は a で始まります。");
    case head2(x) => println("cde は a ではなく " + x + " で始まります。");
  }
... 
private class Head(count : Int) {
  def unapply(value : String) : Option[String] = {
    println("*** unapply of " + count + " called");
    Some(value.substring(0, count));
  }
}

--------------
*** unapply of 1 called
*** unapply of 2 called
cde は a ではなく cd で始まります。
今度は、head1、head2 の unapply がそれぞれ一回づつ実行された。
...
    case head1("a") => println("cde は a で始まります。");
    case head2("b") => println("cde は a で始まります。");
    case head2(x) => println("cde は a ではなく " + x + " で始まります。");
...

------------------------------
*** unapply of 1 called
*** unapply of 2 called
cde は a ではなく cd で始まります。
case 句に head1 が1回、head2 が2回出現しているが unapply はそれぞれ一回ずつしか実行されていないので、複数のオブジェクトが出現する場合にはそれぞれに対して一回ずつ unapply が実行されていることが分かった。

0 件のコメント:

コメントを投稿