2008-10-15 9 views

Répondre

70

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

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, c'est bon pour la lisibilité. Une autre option simule ce que ferait normalement une poursuite à un niveau de bytecode.

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

} 

Un moyen de soutenir la rupture est par des exceptions:

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

Vous pouvez utiliser un type d'exception personnalisée pour ne pas masquer d'autres exceptions réelles, surtout si vous avez d'autres traitements en cours dans ce classe qui pourrait déclencher des exceptions réelles, comme NumberFormatExceptions ou IOExceptions.

+30

Utilisation d'exceptions pour contrôler le flux de programme est une mauvaise idée. La création d'exceptions nécessite de prendre un instantané de la pile des appels, ce qui est coûteux. –

+6

Pas si vous remplacez la méthode qui génère la pile d'appels dans l'exception que vous lancez pour ne rien faire. C'est l'avantage d'une exception personnalisée. – shemnon

+9

C'est aussi une étape supplémentaire dans un effort pour contourner un problème que vous n'auriez pas si vous utilisiez une fermeture comme une fermeture au lieu de la considérer comme une construction en boucle. L'exemple ci-dessus bénéficierait plus de la fixation de l'intention globale peu claire de la logique à soit filtrer les lignes ou trouver une ligne. – Cliff

15

Les fermetures ne peuvent pas se rompre ou se poursuivre car elles ne sont pas des constructions en boucle/itération. Au lieu de cela, ils sont des outils utilisés pour traiter/interpréter/gérer la logique itérative. Vous pouvez ignorer les itérations données par simple retour de la fermeture sans traitement comme dans:

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

} 

soutien Break ne se produit pas au niveau de fermeture mais est sous-entendu par la sémantique de l'appel de méthode accepté la fermeture. En bref, cela signifie au lieu d'appeler "each" sur quelque chose comme une collection qui est destinée à traiter l'ensemble de la collection que vous devriez appeler find qui traitera jusqu'à ce qu'une certaine condition soit remplie. La plupart (toutes?) Fois que 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 qui fait correspondre la méthode de recherche non seulement à vos besoins logiques mais aussi à votre intention. Malheureusement, certaines API manquent de support pour une méthode de recherche ... Fichier par exemple. Il est possible que tout le temps passé à argumenter sur le fait que la langue devrait inclure la pause/continuer aurait pu être bien dépensé en ajoutant la méthode de recherche à ces zones négligées. Quelque chose comme firstDirMatching (Closure c) ou findLineMatching (Closure c) irait un long chemin et répondrait à 99 +% du "pourquoi je ne peux pas rompre avec ...?" questions qui apparaissent dans les listes de diffusion. Cela dit, il est trivial d'ajouter vous-même ces méthodes via MetaClass ou Catégories.

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

autres hacks impliquant des exceptions et autres magie peut fonctionner, mais introduire des frais généraux supplémentaires dans certaines situations et convoluer la lisibilité dans d'autres. La vraie réponse est de regarder votre code et de demander si vous itérez vraiment ou si vous cherchez plutôt.

10

Si vous pré-créez un objet Exception statique dans Java et que vous lâchez l'exception (statique) à l'intérieur d'une fermeture, le coût d'exécution est minimal. Le coût réel est encouru dans la création de l'exception, pas dans le lancement. Selon Martin Odersky (inventeur de Scala), de nombreuses JVM peuvent réellement optimiser les instructions de lancer à des sauts simples.

Ceci peut être utilisé pour simuler une rupture:

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

Ça ... c'est une très bonne idée. Combinez cela avec des énumérations pour créer une exception pré-créée pour chaque énumération. Heck - mettre une méthode dans l'énumération qui le lance. Condition.BAD_WEATHER.fire(); – paulmurray

+1

En fait, l'idée n'était pas la mienne. Martin Odersky (l'inventeur de Scala) l'a montré comme une solution au même problème dans Scala dans un discours qu'il [a donné] (http://www.youtube.com/watch?v=zqFryHC018k&hd=1). J'ai eu un professeur dans un cours de structures de données à Rensselaer il y a plusieurs années montre une solution à un problème qui n'a utilisé aucune instruction 'goto' et ensuite la même solution utilisant' goto's. Ce dernier était ** beaucoup plus court. Son point de vue était que parfois le côté pratique l'emporte sur l'élégance. – Ralph

3

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

7

Utilisez retour-continuer et toute fermeture-pause.

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 
} 

Sortie:

1 
2 
3 
+0

Vous êtes un génie. Soyez prudent avec .any. Si la dernière instruction de la fermeture est associée à 'true', elle s'arrêtera. Si vous voulez votre logique ou que vous voulez que votre fichier soit traité complètement, mettez 'false' comme dernière instruction dans n'importe quelle fermeture. Il m'a fallu quelques jours avant de voir ce post. – user2618844

+1

Si vous remplacez le dernier bloc if avec juste 'line ==" 3 "' je pense que c'est un peu plus clair –

+0

Pourquoi est-ce pas la réponse la plus votée? – psaxton

1

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

Ensuite, vous pouvez remplacer continuer avec un filtre et pause avec takeWhile

Voici un exemple:

import rx.Observable 

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