break
Groovy はandcontinue
をクロージャー内からサポートしていないようです。これをシミュレートする最良の方法は何ですか?
revs.eachLine { line ->
if (line ==~ /-{28}/) {
// continue to next line...
}
}
中断ではなく、きれいに継続することのみをサポートできます。特に eachLine や each のようなものでは。break をサポートできないのは、これらのメソッドがどのように評価されるかに関係しており、メソッドに伝達できるループを終了しないことは考慮されていません。続行をサポートする方法は次のとおりです --
最善のアプローチ (結果の値が必要ないと仮定)。
revs.eachLine { line ->
if (line ==~ /-{28}/) {
return // returns from the closure
}
}
サンプルが本当に単純な場合、これは読みやすさに優れています。
revs.eachLine { line ->
if (!(line ==~ /-{28}/)) {
// do what you would normally do
}
}
もう 1 つのオプションは、バイトコード レベルでの通常の continue の動作をシミュレートします。
revs.eachLine { line ->
while (true) {
if (line ==~ /-{28}/) {
break
}
// rest of normal code
break
}
}
ブレークをサポートする 1 つの可能な方法は、例外を使用することです。
try {
revs.eachLine { line ->
if (line ==~ /-{28}/) {
throw new Exception("Break")
}
}
} catch (Exception e) { } // just drop the exception
NumberFormatExceptions や IOExceptions など、実際の例外をスローする可能性のある他の処理がそのクラスで行われている場合は特に、カスタム例外タイプを使用して、他の実際の例外をマスクしないようにすることをお勧めします。
クロージャーはループ/反復構造ではないため、中断または継続できません。代わりに、反復ロジックを処理/解釈/処理するために使用されるツールです。次のように、処理せずにクロージャーから戻るだけで、特定の反復を無視できます。
revs.eachLine { line ->
if (line ==~ /-{28}/) {
return
}
}
中断のサポートはクロージャー レベルでは発生しませんが、代わりに、クロージャーを受け入れたメソッド呼び出しのセマンティクスによって暗示されます。つまり、コレクション全体を処理することを目的としたコレクションのようなもので「each」を呼び出す代わりに、特定の条件が満たされるまで処理する find を呼び出す必要があります。ほとんどの場合 (すべて?)、クロージャーから抜け出す必要があると感じる場合、本当にやりたいことは、反復中に特定の条件を見つけて、find メソッドを論理的なニーズだけでなく意図にも一致させることです。悲しいことに、いくつかの API は find メソッドのサポートを欠いています... ファイルなど。言語にブレーク/コンティニューを含めるべきかどうかの議論に費やされたすべての時間が、これらの無視された領域に find メソッドを追加することに十分に費やされた可能性があります。firstDirMatching(Closure c) や findLineMatching(Closure c) のようなものは大いに役立ち、「なぜ抜け出せないのか...?」の 99% 以上に答えます。メーリングリストにポップアップする質問。とはいえ、MetaClass または Categories を介してこれらのメソッドを自分で追加するのは簡単です。
class FileSupport {
public static String findLineMatching(File f, Closure c) {
f.withInputStream {
def r = new BufferedReader(new InputStreamReader(it))
for(def l = r.readLine(); null!=l; l = r.readLine())
if(c.call(l)) return l
return null
}
}
}
using(FileSupport) { new File("/home/me/some.txt").findLineMatching { line ==~ /-{28}/ }
例外やその他の魔法を含む他のハックは機能する可能性がありますが、状況によっては余分なオーバーヘッドが発生し、他の状況では可読性が複雑になります。本当の答えは、コードを見て、本当に反復しているのか検索しているのかを尋ねることです。
Java で静的な Exception オブジェクトを事前に作成し、クロージャー内から (静的) 例外をスローすると、実行時のコストは最小限になります。実際のコストは、例外をスローするときではなく、例外を作成するときに発生します。Martin Odersky (Scala の発明者) によると、多くの JVM は実際にスロー命令を単一のジャンプに最適化できます。
これは、休憩をシミュレートするために使用できます。
final static BREAK = new Exception();
//...
try {
... { throw BREAK; }
} catch (Exception ex) { /* ignored */ }
returnを使用して続行し、クロージャーを使用してbreak します。
例
ファイルの内容:
1
2
----------------------------
3
4
5
グルーヴィーなコード:
new FileReader('myfile.txt').any { line ->
if (line =~ /-+/)
return // continue
println line
if (line == "3")
true // break
}
出力:
1
2
3
この場合、おそらくfind()
方法を考える必要があります。渡されたクロージャーが最初に true を返した後に停止します。
rx-javaを使用すると、イテラブルをオブザーバブルに変換できます。
次に、continueをフィルターに置き換え、 takeWhileで中断することができます
次に例を示します。
import rx.Observable
Observable.from(1..100000000000000000)
.filter { it % 2 != 1}
.takeWhile { it<10 }
.forEach {println it}