¿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?


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.


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.


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.


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.


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 .


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

}

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

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:

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

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

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 .


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

"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í.


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

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.


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);
}

-1
  while (i<n) {
    bool shouldBreakOuter = false;
    for (int j=i + 1; j<n; ++j) {
      if (someCondition) {
          shouldBreakOuter = true;
      }
    }

    if (shouldBreakOuter == true)
      break;

  }

-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.


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.


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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.