Domanda

Sembra che Groovy non supporti break e continue all'interno di una chiusura. Qual è il modo migliore per simularlo?

revs.eachLine { line -> 
    if (line ==~ /-{28}/) {
         // continue to next line...
    }
}
È stato utile?

Soluzione

Puoi solo supportare continuare in modo pulito, non interrompere. Soprattutto con cose come eachLine e ognuna. L'incapacità di supportare l'interruzione ha a che fare con il modo in cui questi metodi vengono valutati, non viene presa in considerazione la possibilità di non completare il ciclo che può essere comunicato al metodo. Ecco come supportare continue -

Migliore approccio (supponendo che non sia necessario il valore risultante).

revs.eachLine { line -> 
    if (line ==~ /-{28}/) {
        return // returns from the closure
    }
}

Se il tuo campione è davvero così semplice, questo è buono per la leggibilità.

revs.eachLine { line -> 
    if (!(line ==~ /-{28}/)) {
        // do what you would normally do
    }
}

un'altra opzione, simula cosa farebbe normalmente un proseguimento a livello di bytecode.

revs.eachLine { line -> 
    while (true) {
        if (line ==~ /-{28}/) {
            break
        }
        // rest of normal code
        break
    }

}

Un possibile modo per supportare l'interruzione è tramite le eccezioni:

try {
    revs.eachLine { line -> 
        if (line ==~ /-{28}/) {
            throw new Exception("Break")
        }
    }
} catch (Exception e) { } // just drop the exception

Potresti voler usare un tipo di eccezione personalizzato per evitare di mascherare altre eccezioni reali, specialmente se hai in corso altre elaborazioni in quella classe che potrebbero generare eccezioni reali, come NumberFormatExceptions o IOExceptions.

Altri suggerimenti

Le chiusure non possono rompersi o continuare perché non sono costrutti loop / iterazione. Invece sono strumenti usati per elaborare / interpretare / gestire la logica iterativa. Puoi ignorare determinate iterazioni semplicemente ritornando dalla chiusura senza elaborare come in:

revs.eachLine { line -> 
    if (line ==~ /-{28}/) {
            return
    }

}

Il supporto di interruzione non si verifica a livello di chiusura ma è invece implicito dalla semantica della chiamata del metodo accettata dalla chiusura. In breve, ciò significa invece di chiamare " ciascuno " su qualcosa come una raccolta che ha lo scopo di elaborare l'intera raccolta, dovresti chiamare find, che verrà elaborato fino a quando una determinata condizione è soddisfatta. La maggior parte (tutte?) Le volte che senti la necessità di uscire da una chiusura, quello che vuoi veramente fare è trovare una condizione specifica durante la tua iterazione che renda il metodo find adatto non solo ai tuoi bisogni logici ma anche alle tue intenzioni. Purtroppo alcune API non supportano un metodo find ... File ad esempio. È possibile che tutto il tempo trascorso a discutere se la lingua dovrebbe includere break / continue avrebbe potuto essere ben speso aggiungendo il metodo find a queste aree trascurate. Qualcosa come firstDirMatching (Closure c) o findLineMatching (Closure c) farebbe molta strada e risponderebbe al 99 +% del "perché non posso interrompere da ...?" domande che compaiono nelle mailing list. Detto questo, è banale aggiungere questi metodi da soli tramite MetaClass o Categorie.

class FileSupport {
   public static String findLineMatching(File f, Closure c) {
      f.withInputStream {
         def r = new BufferedReader(new InputStreamReader(it))
         for(def l = r.readLine(); null!=l; l = r.readLine())
             if(c.call(l)) return l
         return null
      }
   }
}

using(FileSupport) { new File("/home/me/some.txt").findLineMatching { line ==~ /-{28}/ }

Altri hack che comportano eccezioni e altre magie possono funzionare ma introducono sovraccarico in alcune situazioni e contorcono la leggibilità in altre. La vera risposta è guardare il tuo codice e chiederti se stai realmente eseguendo iterazioni o ricerche.

Se si pre-crea un oggetto Eccezione statico in Java e quindi si genera l'eccezione (statica) all'interno di una chiusura, il costo di runtime è minimo. Il costo reale è sostenuto per creare l'eccezione, non per lanciarla. Secondo Martin Odersky (inventore di Scala), molte JVM possono effettivamente ottimizzare le istruzioni di lancio ai singoli salti.

Questo può essere usato per simulare un'interruzione:

final static BREAK = new Exception();
//...
try {
  ... { throw BREAK; }
} catch (Exception ex) { /* ignored */ }

Usa ritorna per continua e qualsiasi chiusura per rompere .

Esempio

Contenuto del file:

1
2
----------------------------
3
4
5

Codice Groovy:

new FileReader('myfile.txt').any { line ->
    if (line =~ /-+/)
        return // continue

    println line

    if (line == "3")
        true // break
}

Output:

1
2
3

In questo caso, dovresti probabilmente pensare al metodo find () . Si interrompe dopo la prima volta che la chiusura è passata e ritorna vera.

Con rx-java puoi trasformare un iterabile in un osservabile.

Quindi puoi sostituire continua con un filtro e interruzione con takeWhile

Ecco un esempio:

import rx.Observable

Observable.from(1..100000000000000000)
          .filter { it % 2 != 1} 
          .takeWhile { it<10 } 
          .forEach {println it}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top