uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

1.8.1以降をキャッチアップするシリーズその3、引数無しfind/findAllの新規追加

Groovy 1.8.1以降で追加されたGDKメソッド(主にコレクション関係)を解説するシリーズ第3段、「引数無しfind/findAllの新規追加」です。

引数無しのfind()とfindAll()

Groovy 1.8.1では以下の2つのGDKメソッドが追加されています。

  • public Object find()
  • public Collection findAll()

find(),findAll()はもともと定義されていましたが、これらの引数無しバージョンです。

意味は、引数有りのfind(Closure) / findAll(Closure) のクロージャ引数にClosure.IDENTITYを与えて呼び出した場合と等価です。IDENTITYもClosureクラスに新規追加されたクロージャ定数です。名称はたぶん恒等関数(Identity function)から来ており、要は何も変化しない変換、引数そのままを返すようなクロージャです。Closure.IDENTITYは例えば以下のような感じで定義されています*1

class Closure {
    ..
  static final Closure IDENTITY = { arg -> arg } // もしくは { it }
   ...
}

ここで一旦、クロージャ引数のあるfind(Closure c),findAll(Closure c)の仕様をおさらいしておきましょう。これらの仕様は

  • find(Closure c) :コレクションの要素eのうち、c.call(e)が真と判断される最初のものを返す
  • findAll(Closure c) :コレクションの要素eのうち、c.call(e)が真と判断されるすべての要素からなるコレクションを返す

だったわけですが、引数無しバージョンの意味はそれぞれ

  • find() :コレクションの要素eのうち、IDENTITY.call(e)が真と判断される最初のものを返す
  • findAll() :コレクションの要素eのうち、IDENTITY.call(e)が真と判断されるすべての要素からなるコレクションを返す

となります。なお、find,findAllにおいて、「要素が真(条件を満す)かどうか」の判断は、Groovy Truthで判断されます。つまり、booleanであればtrue、数値であれば0以外、コレクションであれば空ではない、参照型であればnullではないような要素が真と判断されます。結果的に、引数無しfindは「非false、非0、非null、非空‥である最初の要素」、引数無しfindAllは「非false、非0、非null、非空…であるすべての要素」を返すことになります。

以下は例です。

assert [0,null,[],1,3,0,2,4].find() == 1
assert [0,null,[],1,3,0,2,4].findAll() == [1,3,2,4]
// 上は以下と等価
assert [0,null,[],1,3,0,2,4].find(Closure.IDENTITY) == 1
assert [0,null,[],1,3,0,2,4].findAll(Closure.IDENTITY) == [1,3,2,4]
// さらに言えば、以下と等価でもある。
assert [0,null,[],1,3,0,2,4].find{it} == 1
assert [0,null,[],1,3,0,2,4].findAll{it} == [1,3,2,4]

find系は次のfindResultsと合せて1回のエントリにするつもりだったのですが、思いのほか長くなったので続きます〜。

*1:実際にはJavaで定義されている。