ラベル break の投稿を表示しています。 すべての投稿を表示
ラベル break の投稿を表示しています。 すべての投稿を表示
このページの記事一覧
● 2012年8月5日日曜日 - [Scala] tryBreakable
● 2012年8月4日土曜日 - [Scala] Continue
● 2012年8月3日金曜日 - [Scala] break

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 の中であることが分かった。



2012年8月4日土曜日

[Scala] Continue

前回、Scala での Breaks クラスを使った break の仕方を調べてみた。
break と同様に Scala には continue 文もないのだが、Breaks を使うとほぼ同じような書き方で continue 相当のことを実現できる。

まずは、普通に break。
  def breakSample10 {
    import scala.util.control.Breaks;
    val mybreaks = new Breaks;
    import mybreaks.{ break, breakable };

    breakable {
      for (i: Int <- 1 to 5) {
        if (i == 3) break; 
        print(i);
      }
    }
  }
--------------------------------
12

2012年8月3日金曜日

[Scala] break

Scala には break 文がない。
しかし break 用のクラスが用意されていて一応それっぽいコードを書くことができるようになっている。

まずは良く出てくる基本形。

  def breakSample1 {
    import scala.util.control.Breaks;
    val mybreaks = new Breaks;
    import mybreaks.{ break, breakable };

    breakable {
      println("before break");
      break;
      println("after break");
    }
    println("out of break");
  }


before break out of break

多少おまじないがあるが、一見、breakable 構文のようなものがあってそれで括っておけば break できるように見える。

import 文によるメソッド呼び出し先のオブジェクトやメソッド実行時の括弧の省略を一切無くしてみる。

  def breakSample2 {
    val mybreaks : scala.util.control.Breaks 
        = new scala.util.control.Breaks;

    mybreaks.breakable( {
      println("before break");
      mybreaks.break();
      println("after break");
    } );
    println("out of break");
  }

このように書くと、Breaks クラスの mybreaks オブジェクトに対して、 breakable メソッドを ブロック式を引数にして実行しているという構造が一目でわかる。


Breaks の実装


この break はどのように機能しているのだろう。
scala.util.control.Breaks の実装を確認してみる。
以下、scala.util.control.Breaks の実装の抜粋。

class Breaks {
  private val breakException = new BreakControl
  def breakable(op: => Unit) {
    try {
      op
    } catch {
      case ex: BreakControl =>
        if (ex ne breakException) throw ex
    }
  }
  ...
  def break() { throw breakException }
}
object Breaks extends Breaks
private class BreakControl extends ControlThrowable

break 文は専用の例外をスローしているだけ。
breakable 文では引数としてブロック式を受け取ってそれを実行し、 break 文でスローされた専用例外をキャッチしたら握りつぶして終了、という動作になっている。

一見 break っぽくなっているが、早い話が break の代わりに例外をスローして外側でキャッチするという構造になっている。


多重 break


先ほどの Breaks の実装を見ると、breakable でキャッチするのは 同一インスタンスの break でスローされた例外に限定されていることがわかる。
これを利用すると、多重脱出の break も書くことができる。

  def breakSample3 {
    val mybreaks1 = new scala.util.control.Breaks;
    val mybreaks2 = new scala.util.control.Breaks;

    mybreaks1.breakable({
      mybreaks2.breakable({
        println("before break");
        mybreaks1.break();
        println("after break");
      })
      println("out of break2");
    })
    println("out of break1")
  }


before break out of break1

「mybreaks1.break()」の後で、「"out of break2"」を飛ばして一気に「"out of break1"」に到達している。

このように多重で使う場合には、最初の省略型のようにインスタンスまで import してしまうとどちらにbreakable や break を実行しているのかわからなくなるので、Breaks クラスのインスタンスは省略できない。


breakable の無い break


breakable で括らずに break するとどうなるだろうか?

  def breakWithoutBreakable {
    val mybreaks = new scala.util.control.Breaks;
    mybreaks.break();
  }


Exception in thread "main" scala.util.control.BreakControl

スローされている例外を catch していないので当たり前といえば当たり前の結果。


コンパニオンオブジェクトの利用


Breaks にはコンパニオンオブジェクトが定義されているので、 この記事の最初に出てきた例はそのコンパニオンオブジェクトを使うともう少しシンプルになる。

  def breakSample0 {
    import scala.util.control.Breaks;
    import Breaks.{ break, breakable };

    breakable {
      println("before break");
      break;
      println("after break");
    }
    println("out of break");
  }

これならわざわざ Breaks オブジェクトを作る必要はない。
ただし コンパニオンオブジェクトはひとつなので多重 break をする場合には Breaks オブジェクトを自前で作る必要がある。

次回は、この break を使ってcontinue 相当のことを試してみる。