Salir de un bucle anidado

216

Si tengo un bucle for que está anidado dentro de otro, ¿cómo puedo salir de ambos bucles (interno y externo) de la manera más rápida posible?

No quiero tener que usar un booleano y luego decir ir a otro método, sino simplemente ejecutar la primera línea de código después del bucle externo.

¿Cuál es una manera rápida y agradable de hacer esto?

Estaba pensando que las excepciones no son baratas / solo deberían lanzarse en una condición verdaderamente excepcional, etc. Por lo tanto, no creo que esta solución sea buena desde una perspectiva de rendimiento.

No creo que sea correcto aprovechar las nuevas funciones de .NET (métodos anon) para hacer algo que es bastante fundamental.

GurdeepS
fuente
Solo quería asegurarme: ¿por qué quieres hacer esto?
Jon Limjap
3
¿Por qué no quieres usar un booleano? ¿Qué hay de malo en hacer eso?
Anthony
En VB.net puede ajustar una declaración try / finally (sin captura) alrededor de un número arbitrario de bucles, luego "exit try" los saldrá a todos en cualquier momento.
Brain2000

Respuestas:

206

Bueno, gotopero eso es feo, y no siempre es posible. También puede colocar los bucles en un método (o un método anon) y usar returnpara volver al código principal.

    // goto
    for (int i = 0; i < 100; i++)
    {
        for (int j = 0; j < 100; j++)
        {
            goto Foo; // yeuck!
        }
    }
Foo:
    Console.WriteLine("Hi");

vs:

// anon-method
Action work = delegate
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits anon-method
        }
    }
};
work(); // execute anon-method
Console.WriteLine("Hi");

Tenga en cuenta que en C # 7 deberíamos obtener "funciones locales", que (sintaxis tbd, etc.) significa que debería funcionar algo como:

// local function (declared **inside** another method)
void Work()
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits local function
        }
    }
};
Work(); // execute local function
Console.WriteLine("Hi");
Marc Gravell
fuente
49
En este tipo de situación, no creo que usar goto sea peor que el uso normal de algo como break (después de todo, ambas son ramas incondicionales de una etiqueta, es solo que con break la etiqueta está implícita).
Greg Beech
37
a veces goto es menos malvado que las alternativas
seanb
77
@BeowulfOF: la ruptura solo saldrá del bucle interno, no de los bucles interno y externo.
Greg Beech
36
Ir a sí mismo no es feo. Lo que es feo es abusar de goto que da como resultado un código de espagueti. Usar goto para salir del bucle anidado está perfectamente bien. Además, tenga en cuenta que todos los descansos, continuos y retornos, desde el punto de vista de la programación estructural, son apenas mejores que los goto, básicamente son lo mismo, solo que en un mejor empaque. Es por eso que los lenguajes estructurales puros (como el Pascal original) carecen de los tres.
el.pescado
11
@ el.pescado tiene toda la razón: muchos codificadores se han engañado al creer que gotoes dañino per se , mientras que simplemente es el instrumento correcto para hacer algunas cosas, y como muchos instrumentos pueden ser mal utilizados. Esta aversión religiosa contra gotoes francamente bastante estúpida y definitivamente no científica.
o0 '.
96

Adaptación de C # del enfoque que se usa a menudo en C: establecer el valor de la variable del bucle externo fuera de las condiciones del bucle (es decir, para el bucle que usa la variable int a INT_MAX -1menudo es una buena opción):

for (int i = 0; i < 100; i++)
{
    for (int j = 0; j < 100; j++)
    {
        if (exit_condition)
        {
            // cause the outer loop to break:
            // use i = INT_MAX - 1; otherwise i++ == INT_MIN < 100 and loop will continue 
            i = int.MaxValue - 1;
            Console.WriteLine("Hi");
            // break the inner loop
            break;
        }
    }
    // if you have code in outer loop it will execute after break from inner loop    
}

Como dice la nota en el código break, no saltará mágicamente a la próxima iteración del bucle externo, por lo que si tiene código fuera del bucle interno, este enfoque requiere más comprobaciones. Considere otras soluciones en tal caso.

Este enfoque funciona con fory whilebucles pero no funciona foreach. En caso de foreachque no tenga acceso de código al enumerador oculto, no podrá cambiarlo (e incluso si no pudiera IEnumeratortener algún método "MoveToEnd").

Agradecimientos a los autores de comentarios en línea:
i = INT_MAX - 1sugerencia de Meta
for / foreachcomentario de ygoe .
Comentario apropiado IntMaxde jmbpiano
sobre el código después del bucle interno por blizpasta

Nils Pipenbrinck
fuente
@DrG: No funcionará en c # como "La declaración de interrupción termina el ciclo de cierre más cercano o la declaración de cambio en la que aparece". (msdn)
blizpasta
1
@blizpasta Entonces? Si hace que la condición en el bucle externo sea falsa (como lo hizo), saldrá de ambos.
Patrick
51
deberías usar i = INT_MAX - 1; de lo contrario, i ++ == INT_MIN <100 y el ciclo continuará
Meta
66
alguna idea sobre foreach?
ktutnik
44
@ktutnik No funcionará con foreach porque no tendrá acceso de código al enumerador oculto. Además, IEnumerator no tiene algún método "MoveToEnd".
2014
39

Esta solución no se aplica a C #

Para las personas que encontraron esta pregunta a través de otros idiomas, Javascript, Java y D permiten interrupciones etiquetadas y continúa :

outer: while(fn1())
{
   while(fn2())
   {
     if(fn3()) continue outer;
     if(fn4()) break outer;
   }
}
BCS
fuente
40
Es triste que esto no se pueda hacer con C #, ya que muchas veces produciría un código más limpio.
Rickard
17
De hecho, me emocioné por un segundo hasta que me di cuenta de que esto NO era para C #. :(
Arvo Bowen
1
Este concepto también es para PowerShell en caso de que alguien se encuentre con el problema allí. (Simplemente colocan los dos puntos delante del nombre de la etiqueta). Ahora sé que esto no es específico de PowerShell ... Esta sintaxis sería incompatible con las etiquetas Goto disponibles en C #. PHP usa algo más: descanso 3; Ponga el número de niveles después de la declaración de ruptura.
Ygoe
1
Esto es Java no C #
Luca Ziegler
Para aquellos que deseen ver esta característica en C #, coloque sus felicitaciones aquí, por favor 😉 github.com/dotnet/csharplang/issues/869#issuecomment-326606003
m93a
28

Use un protector adecuado en el bucle exterior. Coloque la guardia en el bucle interno antes de romper.

bool exitedInner = false;

for (int i = 0; i < N && !exitedInner; ++i) {

    .... some outer loop stuff

    for (int j = 0; j < M; ++j) {

        if (sometest) {
            exitedInner = true;
            break;
        }
    }
    if (!exitedInner) {
       ... more outer loop stuff
    }
}

O mejor aún, abstraiga el bucle interno en un método y salga del bucle externo cuando devuelva falso.

for (int i = 0; i < N; ++i) {

    .... some outer loop stuff

    if (!doInner(i, N, M)) {
       break;
    }

    ... more outer loop stuff
}
tvanfosson
fuente
44
Excepto que el OP dijo "No quiero tener que usar un booleano".
LeopardSkinPillBoxHat
Muy pascual ... probablemente prefiera usar un goto, aunque normalmente los evito como la peste.
Jonathan Leffler
21

No me cites sobre esto, pero podrías usar goto como se sugiere en el MSDN. Hay otras soluciones, como incluir un indicador que se verifica en cada iteración de ambos bucles. Finalmente, podría usar una excepción como una solución realmente pesada para su problema.

IR:

for ( int i = 0; i < 10; ++i ) {
   for ( int j = 0; j < 10; ++j ) {
      // code
      if ( break_condition ) goto End;
      // more code
   }
}
End: ;

Condición:

bool exit = false;
for ( int i = 0; i < 10 && !exit; ++i ) {
   for ( int j = 0; j < 10 && !exit; ++j ) {
      // code
      if ( break_condition ) {
         exit = true;
         break; // or continue
      }
      // more code
   }
}

Excepción:

try {
    for ( int i = 0; i < 10 && !exit; ++i ) {
       for ( int j = 0; j < 10 && !exit; ++j ) {
          // code
          if ( break_condition ) {
             throw new Exception()
          }
          // more code
       }
    }
catch ( Exception e ) {}
David Rodríguez - dribeas
fuente
2
Estas son todas soluciones alternativas donde sería súper limpio incluir un método y utilizar el retorno temprano
Dustin Getz
3
:) correcto, esa es una solución simple, pero tendrá que pasar todos los datos locales requeridos al método como argumentos ... Este es uno de los pocos lugares donde goto podría ser la solución adecuada
David Rodríguez - dribeas
El método de condición ni siquiera funciona porque "más código" se ejecutará una vez después de salir del bucle interno antes de salir del bucle externo. El método GOTO funciona pero hace exactamente lo que el afiche dijo que no quería hacer. El método de excepción funciona pero es más feo y lento que GOTO.
Programador de Windows el
Destacaría ese punto y coma después de la etiqueta. De esta manera, esa etiqueta puede estar incluso al final de un bloque. +1
Tamas Hegedus
@Windowsprogrammer El OP preguntó: "¿Cuál es una manera rápida y agradable de hacer esto?" Goto es la solución preferida: limpia y concisa, sin involucrar un método separado.
Suncat2000
16

¿Es posible refactorizar el bucle anidado en un método privado? De esa manera, simplemente podría 'regresar' del método para salir del bucle.

NoizWaves
fuente
Con el beneficio
adicional de acortar
C ++ 11 lambdas facilitan esto en algunos casos:[&] { ... return; ... }();
BCS
14

Me parece que a la gente no le gusta mucho una gotodeclaración, así que sentí la necesidad de aclarar esto un poco.

Creo que las 'emociones' que las personas tienen sobre el gotofuturo se reducen a la comprensión del código y (conceptos erróneos) sobre las posibles implicaciones de rendimiento. Antes de responder la pregunta, por lo tanto, primero entraré en algunos de los detalles sobre cómo se compila.

Como todos sabemos, C # se compila en IL, que luego se compila en ensamblador utilizando un compilador SSA. Daré un poco de información sobre cómo funciona todo esto, y luego trataré de responder la pregunta en sí.

De C # a IL

Primero necesitamos una pieza de código C #. Comencemos simple:

foreach (var item in array)
{
    // ... 
    break;
    // ...
}

Lo haré paso a paso para darle una buena idea de lo que sucede debajo del capó.

Primera traducción: desde foreachal forbucle equivalente (Nota: estoy usando una matriz aquí, porque no quiero entrar en detalles de IDisposable, en cuyo caso también tendría que usar un IEnumerable):

for (int i=0; i<array.Length; ++i)
{
    var item = array[i];
    // ...
    break;
    // ...
}

Segunda traducción: el fory breakse traduce a un equivalente más fácil:

int i=0;
while (i < array.Length)
{
    var item = array[i];
    // ...
    break;
    // ...
    ++i;
}

Y tercera traducción (este es el equivalente del código IL): cambiamos breaky whileen una rama:

    int i=0; // for initialization

startLoop:
    if (i >= array.Length) // for condition
    {
        goto exitLoop;
    }
    var item = array[i];
    // ...
    goto exitLoop; // break
    // ...
    ++i;           // for post-expression
    goto startLoop; 

Si bien el compilador hace estas cosas en un solo paso, le brinda información sobre el proceso. El código IL que evoluciona del programa C # es la traducción literal del último código C #. Puede verlo usted mismo aquí: https://dotnetfiddle.net/QaiLRz (haga clic en 'ver IL')

Ahora, una cosa que has observado aquí es que durante el proceso, el código se vuelve más complejo. La forma más fácil de observar esto es por el hecho de que necesitábamos más y más código para lograr lo mismo. También podría argumentar que foreach, for, whiley breakson realmente cortos-manos para goto, lo cual es cierto en parte.

De IL a ensamblador

El compilador .NET JIT es un compilador SSA. No entraré en todos los detalles del formulario SSA aquí y cómo crear un compilador de optimización, es demasiado, pero puedo dar una comprensión básica sobre lo que sucederá. Para una comprensión más profunda, es mejor comenzar a leer sobre la optimización de compiladores (me gusta este libro para una breve introducción: http://ssabook.gforge.inria.fr/latest/book.pdf ) y LLVM (llvm.org) .

Cada compilador de optimización se basa en el hecho de que el código es fácil y sigue patrones predecibles . En el caso de los bucles FOR, utilizamos la teoría de gráficos para analizar ramas y luego optimizamos cosas como cycli en nuestras ramas (por ejemplo, ramas hacia atrás).

Sin embargo, ahora tenemos ramas hacia adelante para implementar nuestros bucles. Como habrás adivinado, este es en realidad uno de los primeros pasos que el JIT solucionará, como este:

    int i=0; // for initialization

    if (i >= array.Length) // for condition
    {
        goto endOfLoop;
    }

startLoop:
    var item = array[i];
    // ...
    goto endOfLoop; // break
    // ...
    ++i;           // for post-expression

    if (i >= array.Length) // for condition
    {
        goto startLoop;
    }

endOfLoop:
    // ...

Como puede ver, ahora tenemos una rama hacia atrás, que es nuestro pequeño bucle. Lo único que sigue siendo desagradable aquí es la rama con la que terminamos debido a nuestra breakdeclaración. En algunos casos, podemos mover esto de la misma manera, pero en otros está ahí para quedarse.

Entonces, ¿por qué el compilador hace esto? Bueno, si podemos desenrollar el ciclo, podríamos ser capaces de vectorizarlo. Incluso podríamos ser capaces de probar que solo se agregan constantes, lo que significa que todo nuestro circuito podría desaparecer en el aire. Para resumir: al hacer que los patrones sean predecibles (al hacer que las ramas sean predecibles), podemos probar que ciertas condiciones se mantienen en nuestro bucle, lo que significa que podemos hacer magia durante la optimización JIT.

Sin embargo, las ramas tienden a romper esos patrones agradables y predecibles, que es algo optimizadores, por lo tanto, un disgusto. Romper, continuar, ir a, todos tienen la intención de romper estos patrones predecibles y, por lo tanto, no son realmente "agradables".

También debe darse cuenta en este punto de que un simple foreaches más predecible que un montón de gotodeclaraciones que van por todas partes. En términos de (1) legibilidad y (2) desde una perspectiva optimizadora, es la mejor solución.

Otra cosa que vale la pena mencionar es que es muy relevante para optimizar los compiladores para asignar registros a las variables (un proceso llamado asignación de registros ). Como sabrás, solo hay un número finito de registros en tu CPU y son, con mucho, las piezas de memoria más rápidas en tu hardware. Las variables utilizadas en el código que se encuentra en el bucle más interno tienen más probabilidades de obtener un registro asignado, mientras que las variables fuera de su bucle son menos importantes (porque este código probablemente se golpea menos).

Ayuda, demasiada complejidad ... ¿qué debo hacer?

La conclusión es que siempre debe usar las construcciones de lenguaje que tiene a su disposición, que generalmente (implícitamente) crearán patrones predecibles para su compilador. Trate de evitar las ramas extrañas si es posible (en concreto: break, continue, gotoo returnen el medio de la nada).

La buena noticia aquí es que estos patrones predecibles son fáciles de leer (para humanos) y fáciles de detectar (para compiladores).

Uno de esos patrones se llama SESE, que significa Single Entry Single Exit.

Y ahora llegamos a la verdadera pregunta.

Imagina que tienes algo como esto:

// a is a variable.

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...

     if (i*j > a) 
     {
        // break everything
     }
  }
}

La forma más fácil de hacer de este un patrón predecible es simplemente eliminar por ifcompleto:

int i, j;
for (i=0; i<100 && i*j <= a; ++i) 
{
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
}

En otros casos, también puede dividir el método en 2 métodos:

// Outer loop in method 1:

for (i=0; i<100 && processInner(i); ++i) 
{
}

private bool processInner(int i)
{
  int j;
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
  return i*j<=a;
}

Variables temporales? ¿Bueno, malo o feo?

Incluso podría decidir devolver un booleano desde dentro del ciclo (pero personalmente prefiero el formulario SESE porque así es como lo verá el compilador y creo que es más limpio de leer).

Algunas personas piensan que es más limpio usar una variable temporal y proponen una solución como esta:

bool more = true;
for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j) 
  {
     // ...
     if (i*j > a) { more = false; break; } // yuck.
     // ...
  }
  if (!more) { break; } // yuck.
  // ...
}
// ...

Personalmente me opongo a este enfoque. Mire nuevamente cómo se compila el código. Ahora piense en lo que esto hará con estos patrones agradables y predecibles. ¿Obtener la imagen?

Bien, déjame explicarlo. Lo que sucederá es que:

  • El compilador escribirá todo como ramas.
  • Como paso de optimización, el compilador realizará un análisis de flujo de datos en un intento de eliminar la morevariable extraña que solo se usa en el flujo de control.
  • Si tiene éxito, la variable moreserá eliminada del programa y solo quedarán ramas. Estas ramas se optimizarán, por lo que obtendrá solo una rama del bucle interno.
  • Si no tiene éxito, la variable moredefinitivamente se usa en el bucle más interno, por lo que si el compilador no la optimiza, tiene una alta probabilidad de ser asignada a un registro (que consume memoria de registro valiosa).

Entonces, para resumir: el optimizador en su compilador tendrá muchos problemas para descubrir que moresolo se usa para el flujo de control, y en el mejor de los casos, lo traducirá a una sola rama fuera del exterior para lazo.

En otras palabras, el mejor de los casos es que terminará con el equivalente de esto:

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...
     if (i*j > a) { goto exitLoop; } // perhaps add a comment
     // ...
  }
  // ...
}
exitLoop:

// ...

Mi opinión personal sobre esto es bastante simple: si esto es lo que pretendíamos desde el principio, hagamos el mundo más fácil tanto para el compilador como para la legibilidad, y escribamos eso de inmediato.

tl; dr:

Línea de fondo:

  • Use una condición simple en su ciclo for si es posible. Apéguese a las construcciones de lenguaje de alto nivel que tenga a su disposición tanto como sea posible.
  • Si todo falla y te quedas con uno gotoo bool more, prefiere el primero.
atlaste
fuente
OTOH: Rara vez tengo el deseo de romper los bucles externos o deseo ir a goto-equiv (y podría decirse que un montón de código que podría escribirse de manera más clara), aunque podría ser más común en otros dominios ... hoy fue un caso así, pero eso se debió en gran parte a yield return. Es decir, si bien es fácil mostrar que hay algunos casos útiles, para la mayoría de los códigos de "nivel de aplicación" es probable que se produzca una frustración muy baja con C #, excluyendo aquellos "recién provenientes de" C / C ++ ;-) También ocasionalmente extraño El "lanzamiento" de Ruby (desenrollado sin excepción) en ocasiones, ya que también cabe en este dominio.
user2864740
1
@ Suncat2000 La simple introducción de más código, como variables y ramas condicionales para evitar el uso de goto, no hace que su código sea más legible. Diría que lo que sucede es exactamente lo contrario: de hecho, su flujo de control contendrá más nodos, lo cual es bastante La definición de complejidad. Simplemente evitarlo para ayudar a la autodisciplina suena contraproducente en términos de legibilidad.
Atlas
@atlaste: Lo siento, permíteme decir mi comentario más claramente. Lo que debería haber dicho fue: Buena explicación. Pero las personas que evitan ir a goto no se trata de rendimiento; se trata de evitar el abuso que causa falta de legibilidad. Podemos suponer que las personas que evitan gotosimplemente no tienen la disciplina para no abusar de él.
Suncat2000
@atlaste, ¿qué idioma es "ackomplish"?
Pacerier
11

factorizar en una función / método y usar retorno temprano, o reorganizar sus bucles en una cláusula while. goto / excepciones / lo que sea, ciertamente no es apropiado aquí.

def do_until_equal():
  foreach a:
    foreach b:
      if a==b: return
Dustin Getz
fuente
10

Solicitó una combinación de rápido, agradable, sin uso de un booleano, sin uso de goto y C #. Has descartado todas las formas posibles de hacer lo que quieres.

La forma más rápida y menos fea es usar un goto.

Programador de Windows
fuente
Totalmente de acuerdo. Introducir un nuevo método para deshacerse de un solo goto es una tontería. Si el compilador no puede alinear esa llamada de método por cualquier razón, terminaremos con una sobrecarga completamente innecesaria de una llamada de método adicional. Lanzar y atrapar excepciones solo para romper el ciclo es a la vez más detallado y ridículamente más costoso.
Zar Shardan
@Windowsprogramm: OP no solicitó "no usar goto". No quería "ir a otro método". La pregunta estaba lejos de descartar todas las formas posibles, pero tienes razón al sugerir que ir a Goto es lo mejor aquí.
Suncat2000
5

A veces es bueno abstraer el código en su propia función y luego usar un retorno temprano; sin embargo, los retornos tempranos son malos:)

public void GetIndexOf(Transform transform, out int outX, out int outY)
{
    outX = -1;
    outY = -1;

    for (int x = 0; x < Columns.Length; x++)
    {
        var column = Columns[x];

        for (int y = 0; y < column.Transforms.Length; y++)
        {
            if(column.Transforms[y] == transform)
            {
                outX = x;
                outY = y;

                return;
            }
        }
    }
}
Cielo
fuente
1
pero esto permite la solución tradicional solicitada por OP
Surya Pratap
2

He visto muchos ejemplos que usan "break" pero ninguno que usa "continue".

Todavía requeriría una bandera de algún tipo en el bucle interno:

while( some_condition )
{
    // outer loop stuff
    ...

    bool get_out = false;
    for(...)
    {
        // inner loop stuff
        ...

        get_out = true;
        break;
    }

    if( get_out )
    {
        some_condition=false;
        continue;
    }

    // more out loop stuff
    ...

}
dviljoen
fuente
1

Desde la primera vez que vi breaken C hace un par de décadas, este problema me ha molestado. Esperaba que alguna mejora del lenguaje tuviera una extensión para romper que funcionaría así:

break; // our trusty friend, breaks out of current looping construct.
break 2; // breaks out of the current and it's parent looping construct.
break 3; // breaks out of 3 looping constructs.
break all; // totally decimates any looping constructs in force.
Jesse C. Slicer
fuente
3
Luego, un programador de mantenimiento insertará otro nivel de anidamiento, reparará algunas de las declaraciones de ruptura y romperá algunas de las otras declaraciones de ruptura. La solución para eso es ir a una etiqueta en su lugar. Eso realmente se ha propuesto, pero los pragmáticos usan ir a una etiqueta en su lugar.
Programador de Windows el
Espera, ¿quién hace más la programación de mantenimiento? :)
Jesse C. Slicer
2
JavaScript incluso ha etiquetado bloques / declaraciones de interrupción. devguru.com/Technologies/ecmascript/quickref/break.html
David Grant
@ Chris Bartow: ¡genial! hizo mi Navidad :) @David Grant: así que parece JS break == C's goto?
Jesse C. Slicer
D ha etiquetado descanso / continúa
BCS
0

Recuerdo de mis días de estudiante que se dijo que es matemáticamente demostrable que puedes hacer cualquier cosa en código sin un goto (es decir, no hay una situación en la que goto sea la única respuesta). Por lo tanto, nunca uso goto's (solo mi preferencia personal, sin sugerir que tengo razón o no)

De todos modos, para salir de los bucles anidados, hago algo como esto:

var isDone = false;
for (var x in collectionX) {
    for (var y in collectionY) {
        for (var z in collectionZ) {
            if (conditionMet) {
                // some code
                isDone = true;
            }
            if (isDone)
                break;
        }
        if (isDone) 
            break;
    }
    if (isDone)
        break;
}

... espero que eso ayude a aquellos que como yo somos "fanboys" anti-goto :)

MG123
fuente
Lamento decírtelo, pero su profesor es el culpable de su condición. Si se hubiera molestado en forzarlo a aprender ensamblaje, entonces sabrá que 'goto' es solo un salto (por supuesto, estoy ignorando el hecho de que esta es una pregunta #).
cmroanirgo
0

Así lo hice yo. Sigue siendo una solución alternativa.

foreach (var substring in substrings) {
  //To be used to break from 1st loop.
  int breaker=1;
  foreach (char c in substring) {
    if (char.IsLetter(c)) {
      Console.WriteLine(line.IndexOf(c));
      \\setting condition to break from 1st loop.
      breaker=9;
      break;
    }
  }
  if (breaker==9) {
    break;
  }
}

Garvit Arora
fuente
0

La forma más fácil de finalizar un doble ciclo sería terminar directamente el primer ciclo

string TestStr = "The frog jumped over the hill";
char[] KillChar = {'w', 'l'};

for(int i = 0; i < TestStr.Length; i++)
{
    for(int E = 0; E < KillChar.Length; E++)
    {
        if(KillChar[E] == TestStr[i])
        {
            i = TestStr.Length; //Ends First Loop
            break; //Ends Second Loop
        }
    }
}
Kyle
fuente
Esto no funciona, ya que al usar break, finalizará el ciclo interno. El bucle externo continuará iterando.
Alex Leo
0

Los bucles se pueden romper utilizando condiciones personalizadas en el bucle, lo que permite tener un código limpio.

    static void Main(string[] args)
    {
        bool isBreak = false;
        for (int i = 0; ConditionLoop(isBreak, i, 500); i++)
        {
            Console.WriteLine($"External loop iteration {i}");
            for (int j = 0; ConditionLoop(isBreak, j, 500); j++)
            {
                Console.WriteLine($"Inner loop iteration {j}");

                // This code is only to produce the break.
                if (j > 3)
                {
                    isBreak = true;
                }                  
            }

            Console.WriteLine("The code after the inner loop will be executed when breaks");
        }

        Console.ReadKey();
    }

    private static bool ConditionLoop(bool isBreak, int i, int maxIterations) => i < maxIterations && !isBreak;   

Con este código tenemos el siguiente resultado:

  • Iteración de bucle externo 0
  • Iteración de bucle interno 0
  • Iteración de bucle interno 1
  • Bucle interno iteración 2
  • Bucle interno iteración 3
  • Iteración de bucle interno 4
  • El código después del bucle interno se ejecutará cuando se rompa
Daniel Fuentes
fuente
0

Otra opción es una función anónima auto invocada . Sin goto, sin etiqueta, sin nueva variable, sin nombre de función nueva y una línea más corta que el ejemplo de método anónimo en la parte superior.

// self-invoked-anonymous-function
new Action(() =>
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits self invoked lambda expression
        }
    }
})();
Console.WriteLine("Hi");
Robert Einhorn
fuente
-3

Lanza una excepción personalizada que salga del bucle externo.

Funciona para for, foreacho para whilecualquier tipo de bucle y cualquier lenguaje que use try catch exceptionbloque

try 
{
   foreach (object o in list)
   {
      foreach (object another in otherList)
      {
         // ... some stuff here
         if (condition)
         {
            throw new CustomExcpetion();
         }
      }
   }
}
catch (CustomException)
{
   // log 
}
Moesio
fuente
-4
         bool breakInnerLoop=false
        for(int i=0;i<=10;i++)
        {
          for(int J=0;i<=10;i++)
          {
              if(i<=j)
                {
                    breakInnerLoop=true;
                    break;
                }
          }
            if(breakInnerLoop)
            {
            continue
            }
        }
mounesh hiremani
fuente
¿Cuál es la diferencia esencial con la respuesta de dviljoen?
Gert Arnold
Eso no funcionará, porque no verifica la condición "breakInnerLoop" en el bucle for externo, por lo que simplemente itera al siguiente bucle, también escribió j y J, y omitió un punto y coma, que no se compilará.
Sebastian
-4

Como veo, usted aceptó la respuesta en la que la persona lo remite a la declaración goto, donde en la programación moderna y en la opinión de expertos goto es un asesino, lo llamamos asesino en la programación que tiene ciertas razones, que no voy a discutir aquí en este punto, pero la solución de su pregunta es muy simple, puede usar una bandera booleana en este tipo de escenario como lo demostraré en mi ejemplo:

            for (; j < 10; j++)
            {
                //solution
                bool breakme = false;
                for (int k = 1; k < 10; k++)
                {
                   //place the condition where you want to stop it
                    if ()
                    {
                        breakme = true;
                        break;
                    }
                }

                if(breakme)
                    break;
               }

Simple y llano. :)

Steve
fuente
lea la pregunta antes de sugerir un descanso. De ahí los votos negativos.
cmroanirgo
1
léelo ahora, pero cuando publiqué esta respuesta, no se agregó la versión editada de no usar boolean, es por eso que publiqué esta respuesta ... ¡pero gracias por votar a favor!
Steve
1
No es una cosa personal. Desafortunadamente, el voto ahora está bloqueado;) Es una parte necesaria de SO para obtener las mejores respuestas (lo que no siempre sucede)
cmroanirgo
-6

¿Incluso miraste la breakpalabra clave? Oo

Esto es solo un pseudocódigo, pero debería poder ver lo que quiero decir:

<?php
for(...) {
    while(...) {
        foreach(...) {
            break 3;
        }
    }
}

Si piensas en breakser una función comobreak() , entonces su parámetro sería el número de bucles para romper. Como estamos en el tercer bucle en el código aquí, podemos separarnos de los tres.

Manual: http://php.net/break

Ingwie Phoenix
fuente
13
No es una pregunta de php.
Zerga
-10

Creo que, a menos que quieras hacer lo "booleano", la única solución es lanzar. ¡Lo que obviamente no deberías hacer ...!

Thomas Hansen
fuente