web-dev-qa-db-fra.com

Meilleur modèle pour simuler "continuer" dans la fermeture Groovy

Il semble que Groovy ne supporte pas break et continue depuis une fermeture. Quelle est la meilleure façon de simuler cela?

revs.eachLine { line -> 
    if (line ==~ /-{28}/) {
         // continue to next line...
    }
}
67
talanb

Vous pouvez seulement soutenir continuer proprement, pas casser. Surtout avec des trucs comme eachLine et each. L'incapacité à prendre en charge la rupture a à voir avec la façon dont ces méthodes sont évaluées, il n'y a aucune considération prise pour ne pas terminer la boucle qui peut être communiquée à la méthode. Voici comment continuer à soutenir -

Meilleure approche (en supposant que vous n'avez pas besoin de la valeur résultante).

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

Si votre échantillon est vraiment aussi simple que cela, c'est bon pour la lisibilité.

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

une autre option, simule ce que ferait normalement une poursuite au niveau du bytecode.

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

}

Une façon possible de prendre en charge la pause est via des exceptions:

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

Vous souhaiterez peut-être utiliser un type d'exception personnalisé pour éviter de masquer d'autres exceptions réelles, en particulier si vous avez d'autres traitements en cours dans cette classe qui pourraient lever de vraies exceptions, comme NumberFormatExceptions ou IOExceptions.

71
shemnon

Les fermetures ne peuvent pas se rompre ou continuer car ce ne sont pas des constructions de boucle/itération. Ce sont plutôt des outils utilisés pour traiter/interpréter/gérer la logique itérative. Vous pouvez ignorer les itérations données en revenant simplement de la fermeture sans traitement comme dans:

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

}

La prise en charge des coupures ne se produit pas au niveau de la fermeture mais est impliquée à la place par la sémantique de l'appel de méthode qui a accepté la fermeture. En bref, cela signifie qu'au lieu d'appeler "chacun" sur quelque chose comme une collection qui est destinée à traiter la collection entière, vous devez appeler find qui traitera jusqu'à ce qu'une certaine condition soit remplie. La plupart du temps, vous ressentez le besoin de rompre avec une fermeture ce que vous voulez vraiment faire est de trouver une condition spécifique pendant votre itération, ce qui fait que la méthode de recherche correspond non seulement à vos besoins logiques mais aussi à votre intention. Malheureusement, certaines des API ne prennent pas en charge une méthode de recherche ... Fichier par exemple. Il est possible que tout le temps passé à discuter de la question de savoir si le langage devrait inclure pause/continuer aurait été bien utilisé pour ajouter la méthode find à ces zones négligées. Quelque chose comme firstDirMatching (fermeture c) ou findLineMatching (fermeture c) irait un long chemin et répondrait à 99 +% du "pourquoi ne puis-je pas rompre avec ...?" des questions qui apparaissent dans les listes de diffusion. Cela dit, il est trivial d'ajouter ces méthodes vous-même via MetaClass ou Categories.

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

D'autres hacks impliquant des exceptions et d'autres magies peuvent fonctionner mais introduisent des frais supplémentaires dans certaines situations et compliquent la lisibilité dans d'autres. La vraie réponse est de regarder votre code et de demander si vous effectuez réellement une itération ou une recherche.

15
Cliff

Si vous pré-créez un objet Exception statique dans Java et que vous lancez ensuite l'exception (statique) depuis l'intérieur d'une fermeture, le coût d'exécution est minime. Le coût réel est encouru pour la création de l'exception Selon Martin Odersky (inventeur de Scala), de nombreuses machines virtuelles Java peuvent en fait optimiser les instructions de lancer en sauts simples.

Cela peut être utilisé pour simuler une pause:

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

Utilisez retour pour continuer et tout fermeture pour rompre .

Exemple

Contenu du fichier:

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

Code Groovy:

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

    println line

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

Production:

1
2
3
8
Michal Z m u d a

Dans ce cas, vous devriez probablement penser à la méthode find(). Il s'arrête après la première fois que la fermeture qui lui est passée est retournée vraie.

3
0rt

Avec rx-Java vous pouvez transformer un itérable en observable.

Ensuite, vous pouvez remplacer continuer par un filtre et rompre avec takeWhile

Voici un exemple:

import rx.Observable

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