¿Puedo usar break para salir de múltiples bucles 'for' anidados?

304

¿Es posible usar la breakfunción para salir de varios forbucles anidados ?

Si es así, ¿cómo harías para hacer esto? ¿Puedes controlar también cuántos bucles breaksalen?

Falso
fuente
1
En lugar de usar break o goto para salir de múltiples bucles anidados, puede encerrar esa lógica particular en una función y usar return para salir de múltiples bucles anidados. Esto mantendrá la estética de su código y le impedirá usar goto, que es una mala práctica de programación.
Rishab Shinghal

Respuestas:

239

AFAIK, C ++ no admite bucles de nombres, como lo hacen Java y otros lenguajes. Puede usar un goto o crear un valor de marca que use. Al final de cada ciclo, verifique el valor del indicador. Si se establece en verdadero, puede salir de esa iteración.

Cullen Walsh
fuente
317
No tenga miedo de usar un gotosi esa es la mejor opción.
jkeys
18
Soy un nuevo programador de C ++ (y uno sin ningún entrenamiento de programación formal), por lo tanto, después de leer sobre los comentarios de la gente en goto. Dudo en usarlo por miedo a que mi programa explote repentinamente y me mate. Aparte de eso, cuando solía escribir programas en mi ti-83 (en aburrida clase de matemáticas, por supuesto), las funciones que proporcionaba el editor básico requerían el uso de goto's.
Faken
26
@Faken: Dos tipos de programadores usan goto: programadores malos y programadores pragmáticos. Los primeros se explican por sí mismos. Este último, en el que encajaría si elige usarlos bien, utiliza un concepto llamado "malvado" cuando es el menor de (dos) males. Lea esto para comprender mejor algunos conceptos de C ++ que puede necesitar usar de vez en cuando (macros, goto's, preprocesador, matrices): parashift.com/c++-faq-lite/big-picture.html#faq-6.15
jkeys
41
@Faken: No hay nada de malo en usar goto. Es mal uso de un goto que es problemático.
Todos
28
@Hooked: Eso es correcto, excepto que usar gotoraramente es la mejor opción. ¿Por qué no poner los bucles en su propia función ( inlinesi te preocupa la velocidad) y returnde esto?
sbi
265

No, no lo estropees con un break. Este es el último bastión restante para el uso de goto.

Henk Holterman
fuente
19
El estándar de codificación MISRA C ++ permite el uso de goto para cubrir este tipo exacto de situación.
Richard Corden
11
Los programadores reales no tienen miedo de usar goto. Lo hice durante 25 años, sin arrepentimientos, ahorró una tonelada de tiempo de desarrollo.
Doug Null
1
Estoy de acuerdo. los gotos son imprescindibles si no tiene 8K píxeles de ancho y necesita llamar a una secuencia de 30 llamadas al sistema operativo Windows.
Michaël Roy
O un simple "retorno" al poner el código en una función.
Tara
68

Solo para agregar una respuesta explícita usando lambdas:

  for (int i = 0; i < n1; ++i) {
    [&] {
      for (int j = 0; j < n2; ++j) {
        for (int k = 0; k < n3; ++k) {
          return; // yay we're breaking out of 2 loops here
        }
      }
    }();
  }

Por supuesto, este patrón tiene ciertas limitaciones y obviamente solo C ++ 11, pero creo que es bastante útil.

Predelnik
fuente
77
Esto podría confundir al lector al pensar que el retorno causa la función de retorno de la lambda, y no la lambda misma.
Xunie
3
@ Xunie: Si tu ciclo es tan complicado que olvidas que estás en una lambda, es hora de ponerlos en una función diferente, pero para casos simples, esto debería funcionar bastante bien.
MikeMB
17
Creo que esta solución es hermosa
tuket el
Puede capturar el lambda en una plantilla RIIA que le da un nombre y lo llama en dtor (también conocido como alcance gaurd). Esto no agregaría ninguna ganancia de rendimiento, pero puede mejorar la legibilidad y reducir el riesgo de perder la función llamada paréntesis.
Red.Wave
56

Otro enfoque para salir de un bucle anidado es factorizar ambos bucles en una función separada, y returndesde esa función cuando desee salir.

Por supuesto, esto trae a colación el otro argumento de si alguna vez debería explícitamente returndesde una función en cualquier lugar que no sea al final.

Greg Hewgill
fuente
77
Eso es un problema de C. Con RIAA el retorno temprano no es un problema, ya que todos los problemas asociados con el retorno temprano se manejan correctamente.
Martin York
44
Entiendo que la aplicación adecuada de RIAA puede resolver el problema de limpieza de recursos en C ++, pero he visto que el argumento filosófico contra el retorno temprano continúa en otros entornos y lenguajes. Un sistema en el que trabajé donde el estándar de codificación prohibía el retorno temprano tenía funciones llenas de variables booleanas (con nombres como continue_processing) que controlaban la ejecución de bloques de código más abajo en la función.
Greg Hewgill
21
¿Qué es la RIAA? ¿Es eso algo como RAII? = D
jkeys
1
Depende de cuántos bucles tiene y qué tan profundo es el nido ... ¿quieres la píldora azul o la píldora roja?
Matt
34

break saldrá solo del bucle más interno que lo contiene.

Puede usar goto para salir de cualquier número de bucles.

Por supuesto, ir a menudo se considera perjudicial .

¿Es apropiado usar la función de interrupción [...]?

Usar break y goto puede hacer que sea más difícil razonar sobre la corrección de un programa. Vea aquí para una discusión sobre esto: Dijkstra no estaba loco .

Karl Voigtland
fuente
16
Una buena respuesta es que explica que el meme "goto es dañino" está fuertemente ligado a la declaración más generalizada "la interrupción del flujo de control es dañina". No tiene sentido decir "goto es dañino", y luego darse la vuelta y recomendar el uso de breako return.
Pavel Minaev
66
@Pavel: breaky returntiene la ventaja de gotoque no necesita buscar una etiqueta para saber a dónde van. Sí, debajo hay algún tipo de goto, pero muy restringido. Son mucho más fáciles de descifrar por el cerebro de coincidencia de patrones de un programador que los no restringidos goto. Por lo tanto, son preferibles.
sbi
@sbi: Cierto, pero el descanso aún no forma parte de la programación estructurada. Es mejor tolerado que a goto.
jkeys
2
@KarlVoigtland el enlace Dijkstra está desactualizado; esto parece estar funcionando: blog.plover.com/2009/07
Aaron Brager
3
No hay absolutamente nada de malo en usar goto en esta situación. Un goto bien ubicado es mucho mejor y más legible que muchas de las soluciones contorsionadas propuestas de otro modo.
James
22

Aunque esta respuesta ya se presentó, creo que un buen enfoque es hacer lo siguiente:

for(unsigned int z = 0; z < z_max; z++)
{
    bool gotoMainLoop = false;
    for(unsigned int y = 0; y < y_max && !gotoMainLoop; y++)
    {
        for(unsigned int x = 0; x < x_max && !gotoMainLoop; x++)
        {
                          //do your stuff
                          if(condition)
                            gotoMainLoop = true;
        }
    }

}
scigor
fuente
55
lo cual es bueno pero aún no tan legible, preferiría ir a ese caso
Петър Петров
2
esto hace que su código sea "bastante" lento porque gotoMainLoopse verifica cada ciclo
Thomas
1
En este caso, el uso de lo real gotohace que el núcleo sea más legible y tenga un mejor rendimiento.
Петър Петров
20

¿Qué tal esto?

for(unsigned int i=0; i < 50; i++)
{
    for(unsigned int j=0; j < 50; j++)
    {
        for(unsigned int k=0; k < 50; k++)
        {
            //Some statement
            if (condition)
            {
                j=50;
                k=50;
            }
        }
    }
}
jebeaudet
fuente
2
enfoque interesante pero definitivamente me gusta la forma en que ered @ inf.ig.sh lo maneja. for (unsigned int y = 0; y <y_max &&! gotoMainLoop; y ++).
Andy
15

Un ejemplo de código con gotouna etiqueta para salir de un bucle anidado:

for (;;)
  for (;;)
    goto theEnd;
theEnd:
Helio Santos
fuente
11

Una buena manera de salir de varios bucles anidados es refactorizar su código en una función:

void foo()
{
    for(unsigned int i=0; i < 50; i++)
    {
        for(unsigned int j=0; j < 50; j++)
        {
            for(unsigned int k=0; k < 50; k++)
            {
                // If condition is true
                return;
            }
        }
    }
}
Deqing
fuente
44
... que no es una opción si tenemos que pasar de 10 a 20 variables para el encuadre de esta función.
Петър Петров
1
@ ПетърПетров luego elija una lambda, que también es mejor, ya que puede definirla exactamente donde la necesita.
DarioP
+1 para lambdas pero revisión en el núcleo del motor del juego donde incluso un cuadro de pila sigue siendo un cuello de botella. Lamento decirlo, pero las lambdas no son tan livianas al menos en MSVC 2010.
Петър Петров
@ ПетърПетров Luego, cambie el par de funciones en una clase y las variables de la pila en miembros privados.
Arthur Tacca
Esto solo complica el código ya complejo :) A veces, goto es la única solución. O si escribe autómatas complejos con la documentación "goto state X", entonces goto está haciendo que el código se lea como está escrito en el documento. Además, C # y go tienen goto con un propósito: ningún lenguaje se completa sin un goto, y los gotos son a menudo las mejores herramientas utilizadas para escribir traductores intermedios o códigos similares a ensamblados.
Петър Петров
5

Goto puede ser muy útil para romper bucles anidados

for (i = 0; i < 1000; i++) {
    for (j = 0; j < 1000; j++) {
        for (k = 0; k < 1000; k++) {
             for (l = 0; l < 1000; l++){
                ....
                if (condition)
                    goto break_me_here;
                ....
            }
        }
    }
}

break_me_here:
// Statements to be executed after code breaks at if condition
Azeemali Hashmani
fuente
3

El breakcomunicado termina la ejecución de la más cercana de cerramiento do, for, switch, o whiledeclaración en la que aparece. El control pasa a la declaración que sigue a la declaración terminada.

de msdn .

Robar
fuente
3

Creo que un goto es válido en esta circunstancia:

Para simular un break/continue , querrías:

Rotura

for ( ;  ;  ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            goto theEnd;
        }
    }
}
theEnd:

Seguir

for ( ;  ; ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            i++;
            goto multiCont;
        }
    }
    multiCont:
}
JuliusAlphonso
fuente
"Continuar" no funcionará allí, porque la expresión de iteración del primer bucle no se ejecutará
Fabio A.
Supongo que el iterador para el primer bucle es i. Por lo tanto, i++antes del goto
JuliusAlphonso
0

Otros lenguajes, como PHP, aceptan un parámetro para la interrupción (es decir, la interrupción 2;) para especificar la cantidad de niveles de bucle anidados de los que desea salir, pero C ++ no lo hace. Tendrá que resolverlo usando un booleano que establezca en falso antes del ciclo, establezca en verdadero en el ciclo si desea romper, más un corte condicional después del ciclo anidado, verificando si el booleano se estableció en verdadero y romper si es así.

Patrick Glandien
fuente
0

Sé que esta es una publicación antigua. Pero sugeriría una respuesta un poco lógica y más simple.

for(unsigned int i=0; i < 50; i++)
    {
        for(unsigned int j=0; j < conditionj; j++)
        {
            for(unsigned int k=0; k< conditionk ; k++)
            {
                // If condition is true

                j= conditionj;
               break;
            }
        }
    }
Aditya Jagtap
fuente
3
Esa no es una solución muy escalable, ya j = conditionjque no funcionará si tiene un predicado complejo en lugar de j < conditionj.
Sergey
0

Divida cualquier número de bucles por una sola boolvariable, vea a continuación:

bool check = true;

for (unsigned int i = 0; i < 50; i++)
{
    for (unsigned int j = 0; j < 50; j++)
    {
        for (unsigned int k = 0; k < 50; k++)
        {
            //Some statement
            if (condition)
            {
                check = false;
                break;
            }
        }
        if (!check)
        {
            break;
        }
    }
    if (!check)
    {
        break;
    }
}

En este código tenemos break;todos los bucles.

vikas bansal
fuente
0

No estoy seguro de si vale la pena, pero puede emular los bucles con nombre de Java con algunas macros simples:

#define LOOP_NAME(name) \
    if ([[maybe_unused]] constexpr bool _namedloop_InvalidBreakOrContinue = false) \
    { \
        [[maybe_unused]] CAT(_namedloop_break_,name): break; \
        [[maybe_unused]] CAT(_namedloop_continue_,name): continue; \
    } \
    else

#define BREAK(name) goto CAT(_namedloop_break_,name)
#define CONTINUE(name) goto CAT(_namedloop_continue_,name)

#define CAT(x,y) CAT_(x,y)
#define CAT_(x,y) x##y

Ejemplo de uso:

#include <iostream>

int main()
{
    // Prints:
    // 0 0
    // 0 1
    // 0 2
    // 1 0
    // 1 1

    for (int i = 0; i < 3; i++) LOOP_NAME(foo)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cout << i << ' ' << j << '\n';
            if (i == 1 && j == 1)
                BREAK(foo);
        }
    }
}

Otro ejemplo:

#include <iostream>

int main()
{
    // Prints: 
    // 0
    // 1
    // 0
    // 1
    // 0
    // 1

    int count = 3;
    do LOOP_NAME(foo)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cout << ' ' << j << '\n';
            if (j == 1)
                CONTINUE(foo);
        }
    }
    while(count-- > 1);
}
HolyBlackCat
fuente
-1
  while (i<n) {
    bool shouldBreakOuter = false;
    for (int j=i + 1; j<n; ++j) {
      if (someCondition) {
          shouldBreakOuter = true;
      }
    }

    if (shouldBreakOuter == true)
      break;

  }
Mennabah
fuente
-3

Puedes usar try ... catch.

try {
    for(int i=0; i<10; ++i) {
        for(int j=0; j<10; ++j) {
            if(i*j == 42)
                throw 0; // this is something like "break 2"
        }
    }
}
catch(int e) {} // just do nothing
// just continue with other code

Si tiene que salir de varios bucles a la vez, a menudo es una excepción.

lawilog
fuente
1
Me gustaría saber la razón por la cual esta respuesta recibe tantos votos negativos.
hkBattousai
66
@hkBattousai La solución tiene votos negativos porque está usando una excepción para controlar el flujo de ejecución. Como su nombre indica, las excepciones solo deben usarse en casos excepcionales.
Helio Santos
44
@HelioSantos ¿No es esta una situación excepcional para la que el idioma no proporciona la solución adecuada?
hkBattousai
8
Las excepciones son lentas.
Gordon
2
El impacto en el rendimiento del lanzamiento es enorme para algo que no es un error irrecuperable el 99% del tiempo.
Michaël Roy
-4

Romper un bucle for es un poco extraño para mí, ya que la semántica de un bucle for generalmente indica que se ejecutará un número específico de veces. Sin embargo, no es malo en todos los casos; si está buscando algo en una colección y quiere romperlo después de encontrarlo, es útil. Sin embargo, la ruptura de bucles anidados no es posible en C ++; es en otros idiomas mediante el uso de un descanso etiquetado. Puede usar una etiqueta y un goto, pero eso podría provocarle acidez estomacal por la noche ... Sin embargo, parece la mejor opción.

Nick Lewis
fuente
11
No es extraño en absoluto. Si está iterando sobre una colección para buscar algo (y no tiene una forma más rápida de búsqueda), no tiene sentido terminar el ciclo. (como un ejemplo)
Joe