Können Sie von einem Groovy "jeder" Verschluss abbrechen?

143

Ist es möglich, breakvon einem Groovy zu kommen .each{Closure}, oder sollte ich stattdessen eine klassische Schleife verwenden?

blechern
quelle

Antworten:

194

Nein, Sie können kein "jedes" abbrechen, ohne eine Ausnahme auszulösen. Sie möchten wahrscheinlich eine klassische Schleife, wenn die Unterbrechung unter bestimmten Bedingungen abgebrochen werden soll.

Alternativ können Sie anstelle eines Verschlusses einen "Find" -Verschluss verwenden und true zurückgeben, wenn Sie eine Pause eingelegt hätten.

Dieses Beispiel wird abgebrochen, bevor die gesamte Liste verarbeitet wird:

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
}

Druckt

1
2
3
4
5

druckt aber nicht 6 oder 7.

Es ist auch sehr einfach, eigene Iteratormethoden mit benutzerdefiniertem Unterbrechungsverhalten zu schreiben, die Abschlüsse akzeptieren:

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
}

Druckt auch:

1
2
3
4
5    
Ted Naleid
quelle
3
Ich habe auch einen Patch an groovy gesendet, der eine findResult-Methode hinzufügt, die einen kurzgeschlossenen Suchvorgang ausführt, der das erste Nicht-Null-Ergebnis aus dem Abschluss zurückgibt. Diese Methode kann verwendet werden, um fast alle Situationen abzudecken, aus denen jemand möglicherweise frühzeitig ausbrechen möchte. Überprüfen Sie den Patch, um zu sehen, ob er akzeptiert und in Groovy gerollt wird (ich hoffe bis 1.8): jira.codehaus.org/browse/GROOVY-4253
Ted Naleid
2
Ich habe diesen Fall ausprobiert: def a = [1, 2, 3, 4, 5, 6, 7, -1, -2] Es wurde 1 2 3 4 5 -1 -2 zurückgegeben. "BreaK" funktioniert also nicht.
Phat H. VU
Richtiger Fund ist die richtige Option, jeder wird nie wieder zurückkehren
Pushkar
ist findbesser als any- siehe die andere Antwort unten von @Michal, dass einer für mich arbeitet
Rhabarber
Ted Naleids Patch zu groovig ist sehr nützlich. Verwenden Sie stattdessen findResult, wenn Sie tatsächlich das Ergebnis der Suchoperation und nicht das Element selbst benötigen. Beispiel: ​def test = [2] test.findResult{ it * 2 }​wird 4 statt 2 zurückgeben
Doug
57

Ersetzen Sie jede Schlaufe durch einen Verschluss.

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

    println element

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

Ausgabe

1
3
Michal Z Muda
quelle
Nur ein Trick. Was passiert, wenn nach "break" eine Anweisung steht? Diese Aussage wird auch nach dem Treffen "Pause" noch ausgeführt.
Phat H. VU
2
@Phat H. VU Ich habe return hinzugefügt. Die Anweisung nach "Pause" wird nicht ausgeführt
Michal Z Muda
1
Danke für deine Antwort. Du hast recht. Ich stimme auch Ihrer Antwort zu. Mehr: Die Methode any ist in DefaultGroovyMethods definiert und eine Prädikatfunktion, die true zurückgibt, wenn ein Element in einer Auflistung den angegebenen Prädikatabschluss erfüllt.
Phat H. VU
Die Verwendung any()auf diese Weise ist etwas irreführend, funktioniert aber auf jeden Fall und gibt Ihnen die Möglichkeit, zu brechen oder fortzufahren .
vegemite4me
1
Wie @ vegemite4me denke ich, dass dies ein "Trick" ist, aber Sie verstehen die Bedeutung von keinem gut. Aus Gründen der Wartungsfreundlichkeit sollten Sie diese Lösung nicht verwenden.
MatRt
11

Nein, Sie können in Groovy nicht ohne Ausnahme von einer Schließung abbrechen. Außerdem sollten Sie keine Ausnahmen für den Kontrollfluss verwenden.

Wenn Sie aus einem Verschluss ausbrechen möchten, sollten Sie sich wahrscheinlich zuerst überlegen, warum Sie dies tun möchten und nicht, wie Sie es tun sollen. Das erste, was zu berücksichtigen ist, könnte die Ersetzung des fraglichen Verschlusses durch eine der (konzeptuellen) Funktionen höherer Ordnung von Groovy sein. Das folgende Beispiel:

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

wird

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

wird

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

das hilft auch Klarheit. Es gibt die Absicht Ihres Codes viel besser an.

Der mögliche Nachteil in den gezeigten Beispielen besteht darin, dass die Iteration im ersten Beispiel nur früh stoppt. Wenn Sie Leistungsüberlegungen haben, möchten Sie diese möglicherweise sofort stoppen.

Für die meisten Anwendungsfälle, die Iterationen beinhalten, können Sie jedoch normalerweise auf eine der Such-, Grep-, Sammel-, Injektions- usw. Methoden von Groovy zurückgreifen. Normalerweise nehmen sie eine "Konfiguration" vor und "wissen" dann, wie die Iteration für Sie durchgeführt wird, damit Sie die imperative Schleife nach Möglichkeit vermeiden können.

Kai Sternad
quelle
1
"Nein, Sie können nicht von einer Schließung in Groovy abbrechen, ohne eine Ausnahme auszulösen
OlliP
2

Nur mit speziellem Verschluss

// 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
}
Meer-kg
quelle
3
und wenn Sie 1 Milliarde Zeilen haben und der innere Abschluss beim ersten Aufruf true zurückgibt, iterieren Sie über 1 Milliarde minus einen Wert. :(
sbglasius
-4

(1..10) .jedes {

if (it <5)

drucke es aus

sonst

falsch zurückgeben

Sagar Mal Shankhala
quelle
2
Dies bricht nicht aus dem each, es druckt einfach keine Werte größer als 4. Das elseist überflüssig, Ihr Code würde das gleiche ohne es tun. Sie können auch beweisen, dass eaches nicht bricht, return falsewenn Sie println "not breaking"kurz nach elseund kurz vor setzen return false.
Stefan van den Akker
Bitte bemühen Sie sich mindestens minimal, Ihren Code für die Lesbarkeit zu formatieren. Es gibt eine visuelle Vorschau, wenn Sie antworten und viele Anweisungen, wie es geht
Mateusz war
-11

Du könntest vorbeischauen RETURN. Beispielsweise

  def a = [1, 2, 3, 4, 5, 6, 7]
  def ret = 0
  a.each {def n ->
    if (n > 5) {
      ret = n
      return ret
    }
  }

Für mich geht das!

Tseveen D.
quelle
6
Genau das hat das OP gefragt, obwohl es offensichtlich nicht das ist, was er meinte.
Szocske
Kann ich eine Beschreibung haben, warum dies negative Stimmen hat? Dies scheint mir das gleiche Konzept zu sein wie das, was die Top-Antwort (mit weniger Erklärung) mit +133 Stimmen sagt.
Skepi
1
@Skepi Sie können nicht über dem Verschluss "brechen". Sie können die anyMethode des Arrays durch Rückgabe brechen false. Sie können die eachMethode nicht auf die gleiche Weise unterbrechen.
Nux