2009-04-17 6 views
24

Lors de l'utilisation de l'algorithme std :: for_each, comment rompre quand une certaine condition est satisfaite?Rupture dans la boucle std :: for_each

+0

Personnellement, je n'aime pas sortir tôt de la boucle. IMO, pour boucle ne doit être utilisé que lorsque vous avez besoin de itérer complètement. Si vous voulez vous interrompre, envisagez d'autres constructions en boucle. –

+4

Essayez: std :: find_if –

Répondre

10

Vous pouvez utiliser l'algorithme find_if, qui arrête et renvoie l'itérateur dans lequel la condition de prédicat appliquée à l'élément itéré retourne true. Votre prédicat doit donc être modifié pour renvoyer un booléen en tant que condition de continuation/pause.

Cependant, il s'agit d'un hack, vous pouvez donc utiliser les algorithmes.

Une autre méthode consiste à utiliser BOOST_FOREACH.

+0

Oui, je peux le faire, mais que se passe-t-il si je veux appliquer des opérations aux éléments de la boucle jusqu'à ce que la condition soit satisfaite? – Naveen

+0

Naveen, qu'est-ce que c'est? vous pouvez faire n'importe quelle opération sur les éléments que ce soit. alors à tout moment vous pouvez "casser"; et la boucle se termine. –

+0

find_if accepte les itérateurs d'entrée, pas les itérateurs de sortie. Ensuite, je recommanderais d'implémenter la boucle à la main ou d'utiliser BOOST_FOREACH. –

2

Vous ne pouvez pas le faire, sauf si vous lancez une exception, ce qui n'est pas une bonne idée car vous ne faites pas de contrôle de flux avec des exceptions.

Mise à jour: apparemment Boost a un for_each_if que peut aider, mais vous n'utilisez pas Boost.

+0

vous n'avez pas besoin de boost pour utiliser for_each_if(). C'est simple à mettre en œuvre. –

+0

Bien, mais la question n'est pas "comment puis-je implémenter for_each_if()". –

+0

for_each_if ne résout pas non plus la question. Ce n'était pas "comment puis-je faire que mon code ne fasse rien sur les éléments après un certain point", mais "comment arrêter complètement l'itération". Il peut y avoir des coûts de performance sérieux pour traverser toute la plage si l'itération a pu être terminée plus tôt. Donc la réponse est des exceptions ou rien. (où rien ne veut dire "refonte afin que vous n'ayez pas à casser, ou n'utilisez pas for_each pour cela".) – jalf

13

Vous pouvez rompre for_each() en lançant une exception de votre foncteur. Cependant, ce n'est souvent pas une bonne idée, et il existe des alternatives.

Vous pouvez conserver l'état dans votre foncteur. Si vous détectez la condition 'break', placez simplement un drapeau dans votre foncteur et ensuite, pour chaque itération suivante, revenez simplement sans faire le truc de votre foncteur. Évidemment, cela n'arrêtera pas l'itération, ce qui pourrait être coûteux pour les grandes collections, mais cela empêchera au moins le travail d'être effectué.

Si votre collection est triée, vous pouvez trouver() l'élément que vous voulez casser, puis faites for_each de begin() à l'élément find() retourné. Pour finir, vous pouvez implémenter un for_each_if(). Cela n'arrêtera pas à nouveau l'itération mais n'évaluera pas votre foncteur qui fait le travail si le prédicat évalue à faux. Voici 2 saveurs de for_each_xxx(), une qui prend une valeur et effectue le travail si l'opérateur ==() évalue à vrai, et un autre qui prend deux foncteurs; celui qui effectue une comparaison ala find_if(), et l'autre qui effectue le travail si l'opérateur de comparaison évalue à vrai.

/* --- 

    For each 
    25.1.1 

     template< class InputIterator, class Function, class T> 
      Function for_each_equal(InputIterator first, InputIterator last, const T& value, Function f) 

     template< class InputIterator, class Function, class Predicate > 
      Function for_each_if(InputIterator first, InputIterator last, Predicate pred, Function f) 

    Requires: 

     T is of type EqualityComparable (20.1.1) 

    Effects:  

     Applies f to each dereferenced iterator i in the range [first, last) where one of the following conditions hold: 

      1: *i == value 
      2: pred(*i) != false 

    Returns:  

     f 

    Complexity: 

     At most last - first applications of f 

    --- */ 

    template< class InputIterator, class Function, class Predicate > 
    Function for_each_if(InputIterator first, 
         InputIterator last, 
         Predicate pred, 
         Function f) 
    { 
     for(; first != last; ++first) 
     { 
      if(pred(*first)) 
       f(*first); 
     } 
     return f; 
    }; 

    template< class InputIterator, class Function, class T> 
    Function for_each_equal(InputIterator first, 
          InputIterator last, 
          const T& value, 
          Function f) 
    { 
     for(; first != last; ++first) 
     { 
      if(*first == value) 
       f(*first); 
     } 
     return f; 
    }; 
+0

Vous pouvez casser en lançant une exception. Mais la solution find() est meilleure dans certains cas au moins – jalf

+0

True; réponse éditée pour incorporer d'autres entrées. –

+0

J'ai repoussé cette réponse juste après avoir lu le code, mais je dois dire que lancer une exception juste pour casser une boucle plus tôt n'est jamais une bonne idée, sauf si vous avez réellement besoin de lancer si la condition est vraie. –

0

Vous déclenchez une exception. Que ce soit ou non une bonne idée est une question de style, pace @Dan, mais peut-être plus d'un problème avec votre conception. for_each est destiné à une sorte de style de programmation fonctionnelle, qui suppose implicitement que votre fonction peut être appliquée uniformément à travers l'ensemble. Donc, si vous avez besoin de casser, cela pourrait être une condition inhabituelle, et donc digne d'une exception.

L'autre solution, et une solution plus "fonctionnelle", consiste à écrire votre fonction de sorte que si elle ne devrait pas avoir d'effet sur certaines applications, alors écrivez-la sans effet. Ainsi, par exemple, si vous avez une fonction de sommation, ajoutez 0 dans les cas où vous auriez "rompu".

+3

Les exceptions de jet pour le contrôle de flux ne sont pas une question de style. C'est une question de goût. Mauvais goût, c'est-à-dire. –

+1

On dirait que c'est une question de fanatisme aveugle plus que de goût. Il fait le travail, plus simple, plus propre et plus élégamment que la plupart des autres solutions. Je suis un peu d'accord avec Charlie à ce sujet. Si vous avez * besoin * de rompre avec un for_each, alors 1) il y a probablement un problème de conception sous-jacent, et 2) le lancement d'une exception est la solution la plus simple. Tant que l'exception peut être si proprement isolée (c'est une seule ligne de code que vous devez inclure dans try/catch), l'utiliser pour le contrôle de flux est tolérable. – jalf

+0

C'est un peu comme le vieux fanatisme anti-goto. Oui, les gotos peuvent être nuisibles, mais parfois dans certaines langues, ils sont les meilleures des options disponibles. –

6

Si vous voulez faire des actions alors que la condition n'est pas satisfaite, peut-être vous avez besoin de changer l'algorithme sur quelque chose comme std::find_if?

+2

C'est la manière facile. Renvoyez juste juste quand vous voulez arrêter. –

4

Comme déjà montré par d'autres, il est seulement réalisable avec des solutions de contournement que IMHO obscurcir le code. Donc, mes suggestions sont de changer le for_each dans une boucle for normale. Cela le rendra plus visible pour les autres que vous utilisez pause (et peut-être même continuer).

20

Vous pouvez utiliser std :: any_of (ou std :: all_of ou std :: none_of), par exemple. comme ceci:

std::vector<int> a; 
// ...  
std::all_of(a.begin(), a.end(), [&](int val) { 
    // return false if you want to break, true otherwise 
    }); 

Cependant, cette solution est inutile (les valeurs de retour ne sont pas vraiment utilisés pour quoi que ce soit), et vous êtes mieux d'écrire vous propre boucle.

+2

La meilleure réponse qui a vraiment aidé – York

+0

Grande réponse ... – Ash

Questions connexes