Puoi rompere da una chiusura "ciascuno" Groovy?


143

È possibile breakda un Groovy .each{Closure}o dovrei usare un loop classico invece?

Risposte:


194

No, non è possibile interrompere un "ciascuno" senza generare un'eccezione. Probabilmente vuoi un loop classico se vuoi che l'interruzione si interrompa in una particolare condizione.

In alternativa, è possibile utilizzare una chiusura "trova" anziché una ciascuno e restituire true quando si sarebbe interrotta.

Questo esempio verrà interrotto prima di elaborare l'intero elenco:

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
}

stampe

1
2
3
4
5

ma non stampa 6 o 7.

È anche molto facile scrivere i tuoi metodi iteratori con un comportamento di interruzione personalizzato che accetta le chiusure:

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
}

Stampa anche:

1
2
3
4
5    

3
Ho anche inviato una patch a Groovy che aggiunge un metodo findResult che esegue una ricerca in cortocircuito che restituisce il primo risultato non nullo dalla chiusura. Questo metodo potrebbe essere utilizzato per coprire quasi tutte le situazioni in cui qualcuno potrebbe voler uscire presto. Controlla la patch per vedere se viene accettata e trasformata in groovy (spero per 1.8): jira.codehaus.org/browse/GROOVY-4253
Ted Naleid,

2
Ho provato questo caso: def a = [1, 2, 3, 4, 5, 6, 7, -1, -2] Ha restituito 1 2 3 4 5 -1 -2. Pertanto, "breaK" non funziona.
Phat H. VU,

La ricerca corretta è l'opzione corretta, ognuna non si romperà mai al ritorno
Pushkar,

è findmeglio di any- vedi l'altra risposta sotto di @Michal che uno funziona per me
Rabarbaro

La toppa di Ted Naleid su Groovy è molto utile. Usa invece findResult se in realtà hai bisogno del risultato dell'operazione find e non dell'elemento stesso. Es: ​def test = [2] test.findResult{ it * 2 }​restituirà 4 invece di 2
Doug

57

Sostituisci ogni anello con qualsiasi chiusura.

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

    println element

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

Produzione

1
3

Solo un trucco. Cosa succede se c'è una dichiarazione dopo "break". Questa affermazione sarà ancora rispettata dopo l'incontro "break".
Phat H. VU,

2
@Phat H. VU Ho aggiunto return. L'affermazione dopo "break" non verrà eseguita
Michal Z muda

1
Grazie per la tua risposta. Hai ragione. Voto anche la tua risposta. Inoltre: il metodo any è definito in DefaultGroovyMethods ed è una funzione predicato che restituisce true se un elemento in una raccolta soddisfa la chiusura predicato fornita.
Phat H. VU,

L'uso any()in questo modo è un po 'fuorviante, ma sicuramente funziona e ti dà la possibilità di rompere o continuare .
vegemite4me,

1
come @ vegemite4me, penso che questo sia un "trucco" ma non capisci bene il significato di nessuno. Per manutenibilità, non utilizzare questa soluzione.
MatRt

11

No, non puoi uscire da una chiusura in Groovy senza lanciare un'eccezione. Inoltre, non dovresti usare eccezioni per il flusso di controllo.

Se ti ritrovi a voler uscire da una chiusura, dovresti probabilmente prima pensare al motivo per cui vuoi farlo e non a come farlo. La prima cosa da considerare potrebbe essere la sostituzione della chiusura in questione con una delle funzioni (concettuali) di ordine superiore di Groovy. Il seguente esempio:

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

diventa

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

diventa

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

che aiuta anche la chiarezza. Dichiara l'intento del tuo codice molto meglio.

Il potenziale svantaggio negli esempi mostrati è che l'iterazione si arresta solo all'inizio del primo esempio. Se si considerano le prestazioni, è possibile interromperlo immediatamente.

Tuttavia, per la maggior parte dei casi d'uso che coinvolgono iterazioni di solito è possibile ricorrere a uno dei metodi di ricerca, grep, raccolta, iniezione, ecc. Di Groovy. Di solito prendono un po 'di "configurazione" e poi "sanno" come eseguire l'iterazione per te, in modo che tu possa effettivamente evitare il loop imperativo ovunque possibile.


1
"No, non puoi uscire da una chiusura in Groovy senza lanciare un'eccezione." Questo è ciò che fa Scala, vedi dev.bizo.com/2010/01/scala-supports-non-local-returns.html
OlliP

2

Usando solo la chiusura speciale

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

3
e se hai 1 miliardo di righe e la chiusura interna ritorna vera alla prima chiamata, ripeterai più di 1 miliardo meno un valore. :(
sbglasio,

-4

(1..10) .Ogni {

if (it <5)

stampalo

altro

restituire false


2
Questo non viene fuori dal each, semplicemente non stampa valori maggiori di 4. elseÈ superfluo, il tuo codice farebbe lo stesso senza di esso. Inoltre, puoi provare a eachnon rompere return falsese metti println "not breaking"subito dopo elsee appena prima return false.
Stefan van den Akker,

Si prega di fare almeno il minimo sforzo per formattare il codice per la leggibilità. C'è un'anteprima visiva quando rispondi e molte istruzioni su come farlo
Mateusz era il

-11

Potresti rompere RETURN . Per esempio

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

Per me funziona!


6
Il che è esattamente ciò che l'OP ha chiesto, anche se ovviamente non è quello che intendeva dire.
Szocske,

Posso avere una descrizione del perché questo ha voti negativi. Questo mi sembra lo stesso concetto di quello che dice la risposta più alta (con meno spiegazioni) con +133 voti.
Skepi,

1
@Skepi Non puoi "rompere" sopra la chiusura. È possibile interrompere il anymetodo dell'array ritornando false. Non è possibile interrompere il eachmetodo allo stesso modo.
Nux,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.