web-dev-qa-db-ja.com

groovyでクロージャから抜け出すことは可能ですか?

グルーヴィーなクロージャから「抜け出す」方法はありますか?.

多分このようなもの:

[1, 2, 3].each { 
  println(it)
  if (it == 2)
    break 
}
26
josh

例外をスローできます。

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 }
8

Groovyが「any」メソッドを実装していることを忘れがちです。

[1, 2, 3].any
{   
   println it
   return (it == 2)
}​
21
J. Skeen

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メソッドから抜け出すことはできません。フロー制御の名前で例外をスローしてキャッチすることはコードの臭いであり、仲間のプログラマーがあなたの手を叩くかもしれないと私は主張しますが。

14
Ben Doerr
8
Robert Munteanu

eachの代わりにanyを使用してみてください

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

結果:1、2、

7
Phat H. VU

他の解決策があります。ただし、each/find/anyのようなグルーヴィーなものは非常にクールです。収まらない場合は、使用しないでください。あなたはまだ普通の古いものを使うことができます

for (def element : list)

特に、メソッドを終了したい場合も。これで、continue/break/returnを自由に使用できます。結果のコードはかっこいいとは言えないかもしれませんが、簡単で理解しやすいものです。

3
Harry Developer

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

// 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

これは、JohnWagenleiterの回答をサポートしています。 Tigerizzyの答えは明らかに間違っています。彼の最初のコードサンプルを実行するか、理論的にはGroovyのドキュメントを読むことで、簡単に反証することができます。 returnは、現在の反復から値(または引数なしでnull)を返しますが、反復を停止しません。クロージャでは、continueのように動作します。

これを理解しないとinjectを使用することはできません。

例外をスローする以外に、「ループを中断する」方法はありません。この目的で例外を使用すると、臭いと見なされます。したがって、Wagenleiterが示唆しているように、ベストプラクティスは、起動する前に反復する要素を除外することですeachまたはその従兄弟の1つ。

2
Sodastream

rx-Java を使用すると、iterableをobservableに変換できます。

次に、continuefilterに置き換え、breaktakeWhile

次に例を示します。

import rx.Observable

Observable.from(1..100000000000000000)
          .filter { it % 2 != 1} 
          .takeWhile { it<10 } 
          .forEach {println it}
0
frhack