倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

Closure クラスの API を使ってみる (3) : 制御構造!? isCase(), directive

今回は Closure クラスに定義されている、制御構造に関するメソッドもしくは制御構造風に使えるメソッドを見ていきます。 今回扱うメソッドと定数は以下の通り:

    boolean isCase(Object candidate)

    int getDirective()
    void setDirective(int directive)

    static int DONE
    static int SKIP

isCase() メソッド


isCase() メソッドは Groovy で特別に使われるメソッドで

  • switch 文
  • in 文
  • コレクションの grep() メソッド

などの制御を行えます。 ここでは switch 文での使用例を見ていきましょう。

Closure の isCase() メソッドは、クロージャを実行した返り値のオブジェクトに対して asBoolean() メソッド呼んだ結果が返されます*1。 これを利用して、switch 文の case 部分にクロージャを書くことができます。 boolean 値を返すクロージャを使って

def i = 3

switch(i){
    case { it % 2 == 1 }:    // it (ここでは変数 i になる)を2で割ったあまりが1なら、この分岐。
        assert true
        break
    default:
        assert false
}

となります。 また、返り値が boolean 値でない場合は asBoolean() が呼ばれます。 ここでは文字列を返すクロージャを使用した場合のサンプルを見ておきましょう(空文字列は false と評価される):

def s = ''

switch(s){
    case { it }:    // s が空文字列なので、この分岐は使われない。
        assert false
        break
    default :
        assert true
}

null が返された場合は(asBoolean() メソッドはないけど) false と判定されるので、例外が投げられる心配はありません。

directive


次は、直接制御構造と関係しているわけではないのですが、クロージャの directive を見ていきます。 クロージャの directive とは、コレクションの要素にクロージャを適用する際に、while や for 文の中で break, continue 文が行うような繰り返しの制御を可能にするものです。 ただし、クロージャの directive で制御できるのは collect() メソッドのみのようで、each() メソッドや find() メソッドなどでは使えません。 使い方はこんな感じ:

def result = (1..<10).collect{ i ->
    if(i % 3 == 0) directive = Closure.DONE
    i
}

assert result == [1, 2, 3]

クロージャの directive プロパティに Closure.DONE (定数)をセットすると、それ以降の要素に対してクロージャが適用されないようになります。 同じことを for 文で書くと、以下のようになります:

def result = []
for(i in 1..<10){
    result << i
    if(i % 3 == 0)break
}

assert result == [1, 2, 3]

for 文の中の要素の追加( result << i)と if 文の順序が逆になっていることに注意。 もしこの順序をもとのままに書くと

def result = []
for(i in 1..<10){
    if(i % 3 == 0)break
    result << i
}

assert result == [1, 2]

となって、結果が異なります(要素 3 がない)。 そもそもクロージャで directive を使うのは Groovy っぽくないコードなようなので使わない方が無難。 大抵の場合 findAll() メソッドなどでフィルターをかければ事足りるかと。

ちなみに、上記の例では Closure.DONE を使いましたが、Closure.SKIP は同じような使い方はできません。 そもそも Closure.SKIP を読み取って制御を行うメソッドはないようで、自分で directive プロパティにセットしたり読み取ったりして使うようです。 詳しくはこちらを参考:

プログラミングGROOVY

プログラミングGROOVY

*1:null, 0, 空文字列、空リスト、空マップなどは false と判定されます。