2012年8月5日日曜日

[Scala] tryBreakable

前々回前回と Scala の break が続いたのでもう一つ。

scala.util.control.Breaks の 実装を見てみると tryBreakable というメソッドがある。
これを使うと break でブロック式を脱出する際に break した際にだけ実行する文を記述することができる。

  def tryBreakableSample {
    import scala.util.control.Breaks;
    import Breaks.{ break, tryBreakable };

    tryBreakable {
      for (i: Int <- 1 to 5) {
        if (i == 3) break; 
        print(i);
      }
    } catchBreak {
      print("breaked")
    }
  }


12breaked

break 後に catchBreak の後のブロックが実行されている。
注意するべきは、try 文の finally 句と違って break しなかった際には実行されないこと。

...
        if (i == 100) break; 
...


12345


tryBreakable ... catchBreak の構造


例によって、省略記法で構造が分かりにくくなっているので省略せずに書いてみる。

  def tryBreakableSample2 {
    import scala.util.control.Breaks;

    Breaks.tryBreakable( {
      for (i: Int <- 1 to 5) {
        if (i == 3) Breaks.break; 
        print(i);
      }
    } ).catchBreak( print("breaked") );
  }  

ブロック式を受け取った tryBreakable が「何か」を返し、 その何かに対してブロック式を受け取る catchBreak メソッドを実行する構造になっていることがわかる。


tryBreakable ... catchBreak の実装


scala.util.control.Breaks の実装を見てみよう。

  trait TryBlock {
    def catchBreak(onBreak: => Unit): Unit
  }

  def tryBreakable(op: => Unit) = new TryBlock {
    def catchBreak(onBreak: => Unit) = try {
      op
    } catch {
      case ex: BreakControl =>
        if (ex ne breakException) throw ex
        onBreak
    }
  }

Breaks.tryBreakable が返しているのは TryBlock トレイトを実装したオブジェクト。 Breaks.tryBreakable が実行しているのはこのオブジェクトを作成して返しているだけ。

TryBlock トレイトはブロック式を受け取る catchBreak だけを持っている。
Breaks.tryBreakable が返す TryBlock トレイトオブジェクトでの catchBreak の実装は、
  • TryBlock オブジェクト生成時に受け取ったブロック式(= tryBreakable の引数)を実行し、
  • BreakControl がスローされたら、catchBreak の引数であるブロック式を実行する。
という作りになっている。

つまり、tryBreakable の引数として渡したブロック式が実行されるのは、 tryBreakable の中ではなく、TryBlock.catchBreak の中であることが分かった。



0 件のコメント:

コメントを投稿