web-dev-qa-db-ja.com

Groovyの「各」クロージャーから抜け出すことはできますか?

Groovy .each{Closure}からbreakすることは可能ですか、それとも代わりにクラシックループを使用する必要がありますか?

141
tinny

いいえ、例外をスローせずに「各」を中止することはできません。特定の条件下でブレークを中止したい場合は、おそらく古典的なループが必要です。

あるいは、それぞれの代わりに「検索」クロージャーを使用し、ブレークを行ったときにtrueを返すこともできます。

この例は、リスト全体を処理する前に中断します。

def a = [1, 2, 3, 4, 5, 6, 7]

a.find { 
    if (it > 5) return true // break
    println it  // do the stuff that you wanted to before break
    return false // keep looping
}

プリント

1
2
3
4
5

6または7は印刷されません。

また、クロージャを受け入れるカスタムのブレーク動作を備えた独自のイテレータメソッドを作成するのは非常に簡単です。

List.metaClass.eachUntilGreaterThanFive = { closure ->
    for ( value in delegate ) {
        if ( value  > 5 ) break
        closure(value)
    }
}

def a = [1, 2, 3, 4, 5, 6, 7]

a.eachUntilGreaterThanFive {
    println it
}

また印刷します:

1
2
3
4
5    
186
Ted Naleid

each loopをanyクロージャーに置き換えます。

def list = [1, 2, 3, 4, 5]
list.any { element ->
    if (element == 2)
        return // continue

    println element

    if (element == 3)
        return true // break
}

出力

1
3
51

いいえ、例外をスローせずにGroovyのクロージャから抜け出すことはできません。また、制御フローに例外を使用しないでください。

閉鎖から抜け出したいと思う場合は、おそらく、どうやってそれをしたいのかではなく、なぜそれをしたいのかを考える必要があります。最初に考慮すべきことは、問題のクロージャーをGroovyの(概念的な)高階関数の1つに置き換えることです。次の例:

for ( i in 1..10) { if (i < 5) println i; else return}

になる

(1..10).each{if (it < 5) println it}

になる

(1..10).findAll{it < 5}.each{println it} 

また、明確化に役立ちます。それはあなたのコードの意図をずっと良く述べています。

示された例の潜在的な欠点は、最初の例では反復が早期に停止することです。パフォーマンスに関する考慮事項がある場合は、その場で停止することをお勧めします。

ただし、反復を伴うほとんどのユースケースでは、通常、Groovyのfind、grep、collect、injectなどのメソッドのいずれかに頼ることができます。彼らは通常、いくつかの「構成」を取り、次に反復をどのように行うかを「知っている」ので、可能な限り命令型ループを実際に回避できます。

10
Kai Sternad

特別なクロージャーを使用するだけ

// 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
}
2
sea-kg