uehaj's blog

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

1.8.1以降をキャッチアップするシリーズその4、findResultsの新規追加

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

findResultsの新規追加

Groovy 1.8.1では新規GDKメソッドfindResultsが追加されました。findResultsは既存メソッド「findResult」の複数版です。findResultとfindResultsの関係は、findとfindAllの関係と同じです。

findResult

なので、まずは既存メソッドであるfindResultの動作をおさらいしてみましょう。

  • findResult(Closure c)
    1. コレクションから、c.call(e)が非nullである最初の要素eを探す
    2. 見付かればc.call(e)を返す
    3. 見付からなければnullを返す

ピンと来ないかもしれませんが、findと対比することで意味が明確になるかもしれません。findの動作は以下のとおりです。

  • find(Closure c)
    1. コレクションから、c.call(e)がGroovy Truthの意味で真となる最初の要素eを探す
    2. 見付かればeを返す
    3. 見付からなければnullを返す

このように、findResultとfindの違いは以下の2点です。

  • findにおいて条件に当てはまるものが発見された場合の返り値は、引数クロージャを適用する前の要素であるのに対し、findResultはクロージャを適用した後のクロージャ返却値が返る
  • 要素が条件を満すかどうかを判定する論理判定において、findはいわゆるGroovy Truthで判断するが、findResultは「非nullかどうか」で判断する。

ちなみに、クロージャを呼び出した結果を返すから「findResult」なわけですね。

以下、使用例。

assert [null, null, "ABC", "DEF"].findResult{it == null?null:"the value = $it"} == "the value = ABC"
assert [null, null, "ABC", "DEF"].find{it == null?null:"the value = $it"} == "ABC"

assert [0, "ABC", "DEF"].findResult{it} == 0
assert [0, "ABC", "DEF"].find{it} == "ABC"

ところで、個人的意見ですが、「こういうときにfindResult(s)はとても便利だ」という例はいまいち思いつきません。findだとクロージャを2回呼ばなければならないところがfindResultだと1回になるケースはありそうですが、具体的にはどういう例があるのだろう。gpathの結果を処理するケースとかであるのかなあ。ご存知の方、教えて欲しいです。

findResults

さて本題のfindResultsですが、findに対するfindAllと同様に、最初の要素ではなく条件にあてはまる全要素からなるコレクションを返します。

つまり、

  • findResults(Closure c)
    1. コレクションから、c.call(e)の結果が非nullであるすべての要素eについて、c.call(e)の値からなるコレクションを返す
    2. 見付からなければ空のコレクションを返す

です。対比のためfindAllも見てみましょう。

  • findAll(Closure c)
    1. コレクションから、c.call(e)の結果がGroovy Truthの意味で真であるすべての要素eについて、eからなるコレクションを返す
    2. 見付からなければ空のコレクションを返す

以下コード例。

assert [0, "ABC", "DEF"].findAll{it instanceof String ? "$it is String" : null} == [ "ABC", "DEF" ]
assert [0, "ABC", "DEF"].findResults{it instanceof String ? "$it is String" : null} == ["ABC is String", "DEF is String" ]

assert [0, null, "ABC", "DEF"].findAll{it} == ["ABC","DEF"]
assert [0, null, "ABC", "DEF"].findResults{it} == [0, "ABC","DEF"]

まとめ

前回解説したfind()/findAll()とも併せて、コレクションクラスのfind系GDKメソッドをまとめるとこうなります。

GDKメソッド 判定に使用するクロージャ クロージャ結果の判定方法 返り値
find(Closure c) 引数c Groovy Truthで判定 クロージャ結果の判定が真である最初の要素
findAll(Closure c) 引数c Groovy Truthで判定 クロージャ結果の判定が真であるすべての要素からなるコレクション
find() Closure.IDENTITY Groovy Truthで判定 クロージャ結果の判定が真である最初の要素(=Groovy Truth的に真である最初の要素)
findAll() Closure.IDENTITY Groovy Truthで判定 クロージャ結果の判定が真であるすべての要素からなるコレクション(=Groovy Truth的に真であるすべての要素からなるコレクション)
findResult(Closure c) 引数c 非nullかどうか クロージャ結果の判定が真である最初の要素をeとするとc.call(e)の値
findResults(Closure c) 引数c 非nullかどうか クロージャ結果の判定が真であるすべての要素eに対してc.call(e)からなるコレクション

太字がGroovy 1.8.1の新規追加メソッドです。

閑話休題。

今回を含めて、解説が結構長くなってるのは、Groovy 1.8.1の新規追加メソッドのAPIリファレンスだけを読んでも「何が変ったのか」を私は把握できなかったためです。findならfind系のメソッド群全体としてどのような機能セットを元々実現していて、その総体に対してどのように欠落や非対称性を補うのか、という視点で見ていかないと、「追加の意味」を私は把握できませんでした。ということで、既存メソッドとの関係性を重視して解説しているつもりなのです。

ということで、続く。残る主なものはcollect系です。

参考リンク