2009-04-17 15 views
24

Mientras uso el algoritmo std :: for_each, ¿cómo me rompo cuando se cumple una determinada condición?Rompiendo en std :: for_each loop

+0

Personalmente, no me gusta salir temprano de for loop. IMO, for loop debe usarse solo cuando necesite iterar por completo. Si desea dividirse, considere otras construcciones de bucle. –

+4

Pruebe: std :: find_if –

Respuesta

10

Puede usar el algoritmo find_if, que detendrá y devolverá el iterador donde la condición de predicado aplicada al elemento iterado devuelve verdadero. Por lo tanto, su predicado debe cambiarse para devolver un booleano como la condición de continuar/interrumpir.

Sin embargo, este es un truco, por lo que puede usar los algoritmos.

Otra forma es usar BOOST_FOREACH.

+0

Sí, puedo hacerlo, pero ¿qué sucede si quiero aplicar algunas operaciones a los elementos en el ciclo hasta que se cumpla la condición? – Naveen

+0

Naveen, ¿qué pasa con eso? puedes hacer cualquier operación sobre los elementos que sea. entonces en cualquier momento puedes "romper"; y el bucle sale. –

+0

find_if acepta iteradores de entrada, no iteradores de salida. Entonces, recomendaría implementar el bucle a mano, o usar BOOST_FOREACH. –

2

No puede hacerlo, a menos que genere una excepción, lo cual no es una buena idea porque no realiza el control de flujo con excepciones.

Actualización: al parecer, Boost tiene un for_each_if que podría ayudar, pero no está utilizando Boost.

+0

no necesita impulso para usar un for_each_if(). Es simple de implementar. –

+0

Correcto, pero la pregunta no es "¿cómo implemento for_each_if()". –

+0

for_each_if no resuelve la pregunta tampoco. No fue "cómo hago que mi código no haga nada en los elementos después de cierto punto", sino "cómo detengo la iteración por completo". Puede haber algunos costos de rendimiento graves al atravesar todo el rango si la iteración podría haber finalizado antes. Entonces la respuesta es excepciones o nada. (donde nada significa "rediseñar para que no tenga que romper, o no use for_each para esto".) – jalf

13

Puedes salirte del for_each() lanzando una excepción desde tu functor. Sin embargo, esto a menudo no es una buena idea, y hay alternativas.

Puede conservar el estado en su functor. Si detecta la condición de "interrupción", simplemente establezca un indicador en su functor y luego, para cada iteración posterior, simplemente vuelva sin hacer lo de su functor. Obviamente, esto no detendrá la iteración, lo que puede ser costoso para colecciones grandes, pero al menos evitará que se realice el trabajo.

Si su colección está ordenada, puede encontrar() el elemento que desea romper, luego haga for_each desde begin() hasta el elemento find() devuelto.

Finalmente, puede implementar un for_each_if(). Esto no detendrá nuevamente la iteración, pero no evaluará su functor, que hace el trabajo si el predicado se evalúa como falso. Aquí hay 2 sabores de for_each_xxx(), uno que toma un valor y realiza el trabajo si operator ==() se evalúa como verdadero, y otro que toma dos funtores; uno que realiza una comparación ala find_if(), y el otro que realiza el trabajo si el operador de comparación lo evalúa como verdadero.

/* --- 

    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

Puede romper lanzando una excepción. Pero la solución de find() es mejor en algunos casos al menos – jalf

+0

Verdadero; respuesta editada para incorporar otra entrada. –

+0

Volví a subir esta respuesta después de leer el código, pero debo decir que lanzar una excepción solo para romper un ciclo temprano nunca es una buena idea, a menos que realmente necesites tirar si se cumple la condición. –

0

Ha realizado una excepción. Si es una buena idea o no, es una especie de pregunta de estilo, pace @ Dan, pero puede ser más un problema con tu diseño. for_each está destinado a un tipo de estilo de programación funcional, que supone implícitamente que su función se puede aplicar uniformemente en todo el conjunto. Por lo tanto, si hace falta romper, podría ser considerado una condición inusual, y por lo tanto digno de una excepción.

La otra solución, y una solución más "funcional", es escribir su función de modo que si no tiene un efecto en algunas aplicaciones, escríbala para que no tenga ningún efecto. Entonces, por ejemplo, si tuvieras una función de suma, haz que agregue 0 en los casos de los que hubieras "roto".

+3

Lanzar excepciones para el control de flujo no es una cuestión de estilo. Es una cuestión de gusto. Mal gusto, eso es. –

+1

Parece que es más una cuestión de fanatismo ciego que de sabor. Hace el trabajo, más simple, más limpio y más elegante que la mayoría de las otras soluciones. Estoy un poco de acuerdo con Charlie en esto. Si * do * necesita romper de un for_each, entonces 1) probablemente haya un problema de diseño subyacente, y 2) lanzar una excepción es la solución más simple. Siempre que la excepción se pueda aislar de forma tan limpia (es una sola línea de código que debe ajustar en try/catch), usarla para controlar el flujo es tolerable. – jalf

+0

Es como el antiguo fanatismo anti-goto. Sí, los gotos pueden ser dañinos, pero a veces en algunos idiomas son la mejor de las opciones disponibles. –

6

Si quiere hacer algunas acciones mientras la condición no está satisfecha, tal vez necesita un algoritmo de cambio en algo como std::find_if?

+2

Esta es la manera fácil. Solo vuelve verdadero cuando quieras detenerlo. –

4

Como ya lo han demostrado otros, solo es posible con soluciones temporales que en mi humilde opinión ofuscar el código.

Así que mi sugerencia es cambiar el for_each en un ciclo for for normal. Esto hará que sea más visible para los demás que está utilizando un descanso (y tal vez incluso continúe).

20

Puede usar std :: any_of (o std :: all_of o std :: none_of) p. Ej. de esta manera:

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

Sin embargo, esta es una solución derrochador (valores de retorno no son realmente utilizados para cualquier cosa), y que está mejor escrito es el propietario bucle.

+2

La mejor respuesta que realmente ayudó – York

+0

Great answer ... – Ash

Cuestiones relacionadas