groovyでクロージャから抜け出すことは可能ですか?
グルーヴィーなクロージャから「抜け出す」方法はありますか?.
多分このようなもの:
[1, 2, 3].each {
println(it)
if (it == 2)
break
}
例外をスローできます。
try {
[1, 2, 3].each {
println(it)
if (it == 2)
throw new Exception("return from closure")
}
} catch (Exception e) { }
また、「findAll」または「grep」を使用してリストを除外し、「each」を使用することもできます。
[1, 2, 3].findAll{ it < 3 }.each{ println it }
Groovyが「any」メソッドを実装していることを忘れがちです。
[1, 2, 3].any
{
println it
return (it == 2)
}
2013年12月5日大幅に編集されました。
尋ねられた質問に答える。
クロージャから抜け出すことは可能ですか?
return
キーワードを発行することで、クロージャから "break"します。しかし、それは与えられた例では役に立ちません。これは、クロージャ(メソッドと考えてください)がコレクション内のすべてのアイテムに対してeach
メソッドによって呼び出されるためです。
この例を実行すると、1、3の順に出力されることがわかります。
[1, 2, 3].each {
if (it == 2) return
println(it)
}
break
のコンテキストでeach
が意味をなさない理由
each
ループから抜け出すことができるように、for
メソッドから抜け出せない理由を理解するには、実際に何が起こっているのかを少し理解する必要があります。これは、コレクションの各メソッドが行うことを大幅に簡略化したものです。
myEach([0,1,3])
void myEach(List things) {
for (i in things) {
myEachMethod(i)
}
}
void myEachMethod(Object it) { // this is your Closure
if(it == 2) return
println it
}
ご覧のとおり、クロージャは基本的に渡すことができる方法です。 Javaの場合と同様に、メソッド呼び出しまたはクロージャ内から中断することはできません。
each
から抜ける代わりに何をすべきか
Groovyでは、このようなプリミティブループは慣用的ではないため、高レベルの抽象化を使用してコードを表現することになっています。あなたが与えた例では、findAllを利用することを検討します。例えば:
[1,2,3].findAll { it < 2 }.each { println it }
これが何が起こっているのかを理解するのに役立つことを願っています。
暗黙の質問に答えます。
提供されたクロージャに対して
Collection.each
の反復から抜け出すことができますか?
John Wagenleitnerが言ったように、例外をスローしてキャッチせずにeach
メソッドから抜け出すことはできません。フロー制御の名前で例外をスローしてキャッチすることはコードの臭いであり、仲間のプログラマーがあなたの手を叩くかもしれないと私は主張しますが。
詳細については、 Groovyクロージャでの続行をシミュレートするための最良のパターン を参照してください。
eachの代わりにanyを使用してみてください
def list = [1, 2, 3, 4, 5, -1, -2]
list.any { element ->
if (element > 3)
return true // break
println element
}
結果:1、2、
他の解決策があります。ただし、each/find/anyのようなグルーヴィーなものは非常にクールです。収まらない場合は、使用しないでください。あなたはまだ普通の古いものを使うことができます
for (def element : list)
特に、メソッドを終了したい場合も。これで、continue/break/returnを自由に使用できます。結果のコードはかっこいいとは言えないかもしれませんが、簡単で理解しやすいものです。
特別なクロージャーを使用するだけ
// declare and implement:
def eachWithBreak = { list, Closure c ->
boolean bBreak = false
list.each() { it ->
if (bBreak) return
bBreak = c(it)
}
}
def list = [1,2,3,4,5,6]
eachWithBreak list, { it ->
if (it > 3) return true // break 'eachWithBreak'
println it
return false // next it
}
これは、JohnWagenleiterの回答をサポートしています。 Tigerizzyの答えは明らかに間違っています。彼の最初のコードサンプルを実行するか、理論的にはGroovyのドキュメントを読むことで、簡単に反証することができます。 returnは、現在の反復から値(または引数なしでnull)を返しますが、反復を停止しません。クロージャでは、continueのように動作します。
これを理解しないとinjectを使用することはできません。
例外をスローする以外に、「ループを中断する」方法はありません。この目的で例外を使用すると、臭いと見なされます。したがって、Wagenleiterが示唆しているように、ベストプラクティスは、起動する前に反復する要素を除外することですeachまたはその従兄弟の1つ。
rx-Java を使用すると、iterableをobservableに変換できます。
次に、continueをfilterに置き換え、breakをtakeWhile
次に例を示します。
import rx.Observable
Observable.from(1..100000000000000000)
.filter { it % 2 != 1}
.takeWhile { it<10 }
.forEach {println it}