Cómo salir de un bucle desde el interior de un interruptor?


Estoy escribiendo un código que se parece a esto:

while(true) {
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        break; // **HERE, I want to break out of the loop itself**
    }
}

¿Hay alguna manera directa de hacer eso?

Sé que puedo usar una bandera y romper el bucle poniendo una interrupción condicional justo después del interruptor. Solo quiero saber si C++ ya tiene alguna construcción para esto.

Author: meagar, 2009-09-14

19 answers

Premisa

El siguiente código debe considerarse de mala forma, independientemente del lenguaje o la funcionalidad deseada:

while( true ) {
}

Argumentos de apoyo

El bucle while( true ) es pobre porque:{[17]]}

  • Rompe el contrato implícito de un bucle while.
    • La declaración de bucle while debe indicar explícitamente la condición de salida only.
  • Implica que gira para siempre.
    • Código dentro el bucle debe ser leído para entender la cláusula de terminación.
    • Bucles que se repiten para siempre evitan que el usuario termine el programa desde dentro del programa.
  • Es ineficiente.
    • Hay múltiples condiciones de terminación de bucle, incluida la comprobación de "true".
  • Es propenso a errores.
    • No puede determinar fácilmente dónde poner el código que siempre se ejecutará para cada iteración.
  • Conduce a código innecesariamente complejo.

Alternativa a "Ir A"

El siguiente código es mejor forma:

while( isValidState() ) {
  execute();
}

bool isValidState() {
  return msg->state != DONE;
}

Ventajas

Sin bandera. No goto. Excepción. Fácil de cambiar. Fácil de leer. Fácil de arreglar. Además el código:

  1. Aísla el conocimiento de la carga de trabajo del bucle del bucle en sí.
  2. Permite a alguien mantener el código para extender fácilmente la funcionalidad.
  3. Permite asignar múltiples condiciones de terminación en un solo lugar.
  4. Separa la cláusula de terminación del código a ejecutar.
  5. Es más seguro para las centrales Nucleares. ;-)

El segundo punto es importante. Sin saber cómo funciona el código, si alguien me pidió que hiciera el bucle principal para que otros subprocesos (o procesos) tengan algo de tiempo de CPU, dos soluciones vienen a la mente:

Opción # 1

Insértese fácilmente la pausa:

while( isValidState() ) {
  execute();
  sleep();
}

Opción # 2

Anular ejecutar:

void execute() {
  super->execute();
  sleep();
}

Este código es más simple (por lo tanto más fácil de leer) que un bucle con un switch incrustado. El método isValidState solo debe determinar si el bucle debe continuar. El caballo de batalla del método debe abstraerse en el método execute, que permite que las subclases anulen el comportamiento predeterminado (una tarea difícil que usa switch y goto incrustados).

Ejemplo de Python

Contraste la siguiente respuesta (a una Pregunta de Python) que se publicó en StackOverflow:

  1. Bucle para siempre.
  2. Pida al usuario que introduzca su elección.
  3. Si la entrada del usuario es 'restart', continúe el bucle para siempre.
  4. De lo contrario, dejar de bucle para siempre.
  5. Fin.
Codificar
while True: 
    choice = raw_input('What do you want? ')

    if choice == 'restart':
        continue
    else:
        break

print 'Break!' 

Versus:

  1. Inicializa la elección del usuario.
  2. Bucle mientras que la elección del usuario es la palabra 'reiniciar'.
  3. Pida al usuario que ingrese su opción.
  4. Fin.
Codificar
choice = 'restart';

while choice == 'restart': 
    choice = raw_input('What do you want? ')

print 'Break!'

Aquí, while True resulta en código engañoso y excesivamente complejo.

 54
Author: Dave Jarvis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-26 22:49:44

Puede usar goto.

while ( ... ) {
   switch( ... ) {
     case ...:
         goto exit_loop;

   }
}
exit_loop: ;
 140
Author: Mehrdad Afshari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-14 06:53:40

Una solución alternativa es usar la palabra clave continue en combinación con break, es decir:

for (;;) {
    switch(msg->state) {
    case MSGTYPE
        // code
        continue; // continue with loop
    case DONE:
        break;
    }
    break;
}

Use la instrucción continue para terminar cada etiqueta de caso donde desea que el bucle continúe y use la instrucción break para terminar las etiquetas de caso que deben terminar el bucle.

Por supuesto, esta solución solo funciona si no hay código adicional para ejecutar después de la instrucción switch.

 47
Author: sakra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-14 07:41:52

Una manera neatish de hacer esto sería poner esto en una función:

int yourfunc() {

    while(true) {

        switch(msg->state) {
        case MSGTYPE: // ... 
            break;
        // ... more stuff ...
        case DONE:
            return; 
        }

    }
}

Opcionalmente (pero 'malas prácticas'): como ya se ha sugerido, podría usar un goto, o lanzar una excepción dentro del interruptor.

 20
Author: Alterlife,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-14 07:53:54

AFAIK no hay "doble ruptura" o construcción similar en C++. El más cercano sería un goto - que, si bien tiene una mala connotación a su nombre, existe en el lenguaje por una razón - siempre y cuando se use con cuidado y moderación, es una opción viable.

 12
Author: Amber,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-20 00:53:58

Usted podría poner su interruptor en una función separada como esta:

bool myswitchfunction()
{
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        return false; // **HERE, I want to break out of the loop itself**
    }
    return true;
}

while(myswitchfunction())
    ;
 8
Author: Adam Pierce,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-14 07:35:58

Incluso si no le gusta goto, no use una excepción para salir de un bucle. El siguiente ejemplo muestra lo feo que podría ser:

try {
  while ( ... ) {
    switch( ... ) {
      case ...:
        throw 777; // I'm afraid of goto
     }
  }
}
catch ( int )
{
}

Usaría gotocomo en esta respuesta. En este caso goto hará que el código sea más claro que cualquier otra opción. Espero que esta pregunta sea útil.

Pero creo que usar goto es la única opción aquí debido a la cadena while(true). Deberías considerar refactorizar tu bucle. Supongo que lo siguiente solución:

bool end_loop = false;
while ( !end_loop ) {
    switch( msg->state ) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        end_loop = true; break;
    }
}

O incluso lo siguiente:

while ( msg->state != DONE ) {
    switch( msg->state ) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
}
 7
Author: Kirill V. Lyadvinsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:33

No hay una construcción C++ para romper el bucle en este caso.

Use una bandera para interrumpir el bucle o (si es apropiado) extraiga su código en una función y use return.

 5
Author: sharptooth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-14 06:59:56

Podría usar goto, pero preferiría establecer una bandera que detenga el bucle. Entonces sal del interruptor.

 2
Author: Martin York,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-21 00:25:38

¿Por qué no simplemente arreglar la condición en su bucle while, haciendo que el problema desaparezca?

while(msg->state != DONE)
{
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        // We can't get here, but for completeness we list it.
        break; // **HERE, I want to break out of the loop itself**
    }
}
 2
Author: Mark B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-15 15:22:05

No, C++ no tiene una construcción para esto, dado que la palabra clave "break" ya está reservada para salir del bloque switch. Alternativamente un do..while () con un indicador de salida podría ser suficiente.

do { 
    switch(option){
        case 1: ..; break;
        ...
        case n: .. ;break;
        default: flag = false; break;
    }
} while(flag);
 2
Author: Prashanth Sriram,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-07 06:37:33

Creo que;

while(msg->state != mExit) 
{
    switch(msg->state) 
    {
      case MSGTYPE: // ...
         break;
      case DONE:
      //  .. 
      //  ..
      msg->state =mExit;
      break;
    }
}
if (msg->state ==mExit)
     msg->state =DONE;
 1
Author: ercan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-07 08:52:30

La forma más sencilla de hacerlo es poner un simple IF antes de hacer el SWITCH , y que SI pruebe su condición para salir del bucle .......... tan simple como puede ser

 0
Author: SpiriAd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-12 09:06:38

La palabra clave break en C++ solo termina la iteración de encierro más anidada o la instrucción switch. Por lo tanto, no podría romper el bucle while (true) directamente dentro de la instrucción switch; sin embargo, podría usar el siguiente código, que creo que es un excelente patrón para este tipo de problema:

for (; msg->state != DONE; msg = next_message()) {
    switch (msg->state) {
    case MSGTYPE:
        //...
        break;

    //...
    }
}

Si necesita hacer algo cuando msg->state es igual a DONE (como ejecutar una rutina de limpieza), coloque ese código inmediatamente después del bucle for; es decir, si actualmente tienen:

while (true) {
    switch (msg->state) {
    case MSGTYPE:
        //... 
        break;

    //...

    case DONE:
        do_cleanup();
        break;
    }

    if (msg->state == DONE)
        break;

    msg = next_message();
}

Entonces use en su lugar:

for (; msg->state != DONE; msg = next_message()) {
    switch (msg->state) {
    case MSGTYPE:
        //...
        break;

    //...
    }
}

assert(msg->state == DONE);
do_cleanup();
 0
Author: Daniel Trebbien,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-04 15:32:19

Me sorprende lo simple que es esto considerando la profundidad de las explicaciones... Aquí está todo lo que necesitas...

bool imLoopin = true;

while(imLoopin) {

    switch(msg->state) {

        case MSGTYPE: // ... 
            break;

        // ... more stuff ...

        case DONE:
            imLoopin = false;
            break;

    }

}

LOL!! ¡En serio! ¡Eso es todo lo que necesitas! Una variable adicional!

 0
Author: MarqueIV,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-04 07:57:39
while(MyCondition) {
switch(msg->state) {
case MSGTYPE: // ... 
    break;
// ... more stuff ...
case DONE:
   MyCondition=false; // just add this code and you will be out of loop.
    break; // **HERE, you want to break out of the loop itself**
}
}
 0
Author: 01010111 01010011,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-22 07:44:28

Tengo el mismo problema y resuelto usando una bandera.

bool flag = false;
while(true) {
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        flag = true; // **HERE, I want to break out of the loop itself**
    }
    if(flag) break;
}
 0
Author: Ram Suthar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-08 11:00:50

Si recuerdo bien la sintaxis de C++, puede agregar una etiqueta a las sentencias break, al igual que para goto. Así que lo que quieres se escribiría fácilmente:

while(true) {
    switch(msg->state) {
    case MSGTYPE: // ...
        break;
    // ... more stuff ...
    case DONE:
        break outofloop; // **HERE, I want to break out of the loop itself**
    }
}

outofloop:
// rest of your code here
 -2
Author: Andrei Vajna II,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-14 17:13:56
  while(true)
  {
    switch(x)
    {
     case 1:
     {
      break;
     }
    break;
   case 2:
    //some code here
   break;
  default:
  //some code here
  }
}
 -3
Author: Chevaughn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-30 21:35:18