2009-08-26 12 views

risposta

8

È possibile generare un'eccezione:

try { 
    [1, 2, 3].each { 
     println(it) 
     if (it == 2) 
      throw new Exception("return from closure") 
    } 
} catch (Exception e) { } 

Usa potrebbe anche usare "findAll" o "grep" per filtrare l'elenco e poi usare "ogni ".

[1, 2, 3].findAll{ it < 3 }.each{ println it } 
+8

Non siete d'accordo che l'utilizzo di Eccezioni per il controllo delle operazioni di flusso è in genere considerato una scelta scadente o un odore di codice? –

+1

Sì. Ma è un modo per uscire dalla chiusura. Naturalmente, di solito ci sono metodi più appropriati come trovare o trovare Tutto. –

12

12/05/2013 Molto modificato.

Rispondere alla domanda che è stata posta.

È possibile uscire da una chiusura?

Si potrebbe "rompere" da una chiusura emettendo la parola chiave return. Tuttavia ciò non è utile nell'esempio fornito. La ragione di ciò è che la chiusura (pensiamo ad essa come un metodo) viene chiamata dal metodo each per ogni elemento della raccolta.

Se si esegue questo esempio si vedrà che stamperà 1 quindi 3.

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

Perché break nel contesto each non ha senso.

Per capire perché non è possibile uscire dal metodo each come si potrebbe uscire da un ciclo for è necessario capire un po 'di ciò che sta effettivamente accadendo. Ecco una grossolana semplificazione di ciò che fa ogni metodo su una collezione.

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 
} 

Come si può vedere la chiusura è fondamentalmente un metodo che può essere passato in giro. Proprio come in Java non puoi rompere dall'interno della chiamata o chiusura del metodo.

Cosa fare invece di interrompere da each.

In Groovy si deve esprimere il proprio codice utilizzando astrazioni di alto livello in quanto tale loop primitivo non è idiomatico. Per l'esempio che hai dato, prenderei in considerazione l'idea di findAll. Ad esempio:

[1,2,3].findAll { it < 2 }.each { println it } 

Spero che questo ti aiuti a capire cosa sta succedendo.

Risposta alla domanda implicita.

Puoi rompere le iterazioni Collection.each rispetto alla chiusura fornita?

Non si può uscire dal metodo each senza lanciare e rilevare un'eccezione come ha detto John Wagenleitner. Anche se direi che lanciare e catturare un'eccezione nel nome del controllo di flusso è un odore di codice e un collega programmatore potrebbe schiaffeggiare le mani.

+0

Il vostro codice stampa 1, 2, 3 - il ritorno non ritorna da una chiusura. –

+1

Volevo solo chiarire che il ritorno non si "spezzerà" dalla chiusura, restituirà solo il metodo che esegue la chiusura. Nel caso precedente, se ci fosse stata una stampa al di sotto del ritorno, sarebbe stata stampata solo per 1 e 3, tuttavia 1, 2 e 3 sarebbero stati ancora stampati dal primo println. –

+0

Penso che tu sia confuso. "each" è un METODO che accetta una chiusura come argomento. Quindi itera sulla raccolta chiamando la chiusura e passando l'oggetto corrente come argomento alla chiusura. Ho aggiornato la mia risposta per renderlo più chiaro. –

21

Spesso dimentico che Groovy implementa un metodo "qualsiasi".

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

Questo è a sostegno della risposta di John Wagenleiter. La risposta di Tigerizzy è assolutamente sbagliata. Può essere facilmente smentito praticamente eseguendo il suo primo esempio di codice o teoricamente leggendo la documentazione di Groovy. A restituisce restituisce un valore (o null senza argomento) dall'iterazione corrente, ma non interrompe l'iterazione. In una chiusura si comporta piuttosto come continua.

Non è possibile utilizzare iniettare senza comprenderlo.

Non esiste un modo per "interrompere il ciclo" tranne per l'eccezione. L'utilizzo di eccezioni a questo scopo è considerato maleodorante. Quindi, proprio come suggerisce Wagenleiter, la pratica migliore è quella di filtrare gli elementi che si desidera ripetere prima di avviare ogni o uno dei suoi cugini.

7

Cercare di utilizzare qualsiasi invece di ogni

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

Il risultato: 1, 2, 3

1

Proprio utilizzando speciali Chiusura

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

Con rx-java voi può trasformare un iterabile in un obso rvable.

Poi si può sostituire continuano con un filtro di e rottura con TakeWhile

Ecco un esempio:

import rx.Observable 

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

C'è un altra soluzione. Anche se quella roba groovy come ogni/find/any è piuttosto interessante: se non va bene, non usarla. È ancora possibile utilizzare il vecchio vecchio

for (def element : list) 

Soprattutto, se si vuole lasciare il metodo, anche. Ora sei libero di usare continue/break/return come preferisci. Il codice risultante potrebbe non essere bello, ma è facile e comprensibile.

+0

Vorrei andare oltre: utilizzare la soluzione più semplice possibile a meno che non si riesca a vedere una ragione per cui no. Se sei super-fluente in Groovy puoi trovare che usare 'any' qui con' return true' è semplice come questo. Ma il punto di chiusura è che sono progettati per essere passati in giro e manipolati. Per un semplice lavoro, uno strumento semplice e vecchio andrà benissimo! –

Problemi correlati