ループでbreakステートメントを使用する場合と同様に、クロージャーから戻りたいと思います。
例えば:
largeListOfElements.each{ element->
if(element == specificElement){
// do some work
return // but this will only leave this iteration and start the next
}
}
上記のifステートメントでは、リスト全体の反復を停止し、不要な反復を避けるためにクロージャーを残したいと思います。
クロージャ内で例外がスローされて外部でキャッチされるソリューションを見てきましたが、そのソリューションはあまり好きではありません。
この種のアルゴリズムを回避するようにコードを変更する以外に、これに対する解決策はありますか?
それぞれの代わりにfindを使用したいと思います(少なくとも指定された例では)。クロージャーは直接ブレークをサポートしていません。
カバーの下では、groovyは実際にはfindにもクロージャーを使用せず、forループを使用します。
または、条件付きテストクロージャーと、一致が見つかった場合に呼び出す別のクロージャーを使用する独自の拡張バージョンのfind/eachイテレーターを作成して、一致した場合にブレークするようにすることもできます。
次に例を示します。
Object.metaClass.eachBreak = {ifClosure、workClosure-> for(Iterator iter = delegate.iterator(); iter.hasNext();){ def value = iter .next() if(ifClosure.call(value)){ workClosure.call(value) break } } } def a = ["foo"、 "bar"、 "baz"、 "qux"] a.eachBreak({it .startsWith( "b")}){ println "working on $ it" } //「printing on bar」
あなたは間違ったレベルの抽象化に取り組んでいると思います。 .each
ブロックは、それが言っていることを正確に実行します。それは、各要素に対して一度クロージャを実行します。代わりにおそらく必要なのは、List.indexOf
で正しいspecificElement
を見つけ、その上で必要な作業を行います。
特定の要素が見つかるまですべての要素を処理したい場合は、次のようにすることもできます。
largeListOfElements.find { element ->
// do some work
element == specificElement
}
これは、どのような「ブレーク条件」でも使用できます。これを使用して、コレクションの最初のn個の要素を返すことで処理しました
counter++ >= n
閉鎖の終わりに。
私がgroovyを理解しているように、これらの種類のループをショートカットする方法は、ユーザー定義の例外をスローすることです。構文がどうなるかわかりませんが(grorovyプログラマーではありません)、groovyはJVMで実行されるため、次のようなものになります。
class ThisOne extends Exception {Object foo; ThisOne(Object foo) {this.foo=foo;}}
try { x.each{ if(it.isOk()) throw new ThisOne(it); false} }
catch(ThisOne x) { print x.foo + " is ok"; }
Paulmurrayの回答の後で、クロージャー内からスローされた例外で何が起こるか自分自身がわからなかったので、考えやすいJUnitテストケースを作成しました。
class TestCaseForThrowingExceptionFromInsideClosure {
@Test
void testEearlyReturnViaException() {
try {
[ 'a', 'b', 'c', 'd' ].each {
System.out.println(it)
if (it == 'c') {
throw new Exception("Found c")
}
}
}
catch (Exception exe) {
System.out.println(exe.message)
}
}
}
上記の出力は次のとおりです。
a
b
c
Found c
ただし、「フロー制御に例外を使用しないでください」を覚えておいてください。特に、このスタックオーバーフローの質問を参照してください。 例外を通常どおりに使用しない理由制御の流れ?
したがって、上記の解決策は、いずれにしても理想的とは言えません。ただ使用する:
class TestCaseForThrowingExceptionFromInsideClosure {
@Test
void testEarlyReturnViaFind() {
def curSolution
[ 'a', 'b', 'c', 'd' ].find {
System.out.println(it)
curSolution = it
return (it == 'c') // if true is returned, find() stops
}
System.out.println("Found ${curSolution}")
}
}
上記の出力も次のとおりです。
a
b
c
Found c
今日、私は各クロージャーで作業しているときに同様の問題に直面しました。自分の状態で実行の流れを壊したかったのですが、できませんでした。
Groovyで行う最も簡単な方法は、何らかの条件に基づいてブール値を返したい場合に、リストの代わりにany()をリストで使用することです。