c++ - sirve - ¿Cómo salir de un bucle desde el interior de un interruptor?
sentencia break en java (19)
Premisa
El siguiente código se debe considerar de mala calidad, independientemente del idioma o la funcionalidad deseada:
while( true ) {
}
Argumentos de apoyo
El ciclo while( true )
es de forma pobre porque:
- Rompe el contrato implícito de un ciclo while.
- La declaración while loop debería indicar explícitamente la única condición de salida.
- Implica que se repite para siempre.
- El código dentro del ciclo debe leerse para comprender la cláusula de terminación.
- Los bucles que se repiten para siempre evitan que el usuario finalice el programa desde dentro del programa.
- Es ineficiente
- Hay varias condiciones de terminación de bucle, incluida la comprobación de "verdadero".
- Es propenso a errores.
- No se puede determinar fácilmente dónde colocar el código que siempre se ejecutará para cada iteración.
- Conduce a un 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
. Sin excepción. Fácil de cambiar Fácil de leer. Fácil de arreglar Además el código:
- Aísla el conocimiento de la carga de trabajo del bucle desde el bucle mismo.
- Permite a alguien que mantiene el código extender fácilmente la funcionalidad.
- Permite asignar múltiples condiciones de terminación en un solo lugar.
- Separa la cláusula de terminación del código para ejecutar.
- Es más seguro para las plantas de energía nuclear. ;-)
El segundo punto es importante. Sin saber cómo funciona el código, si alguien me pide que haga el ciclo principal para que otros hilos (o procesos) tengan algo de tiempo de CPU, vienen a la mente dos soluciones:
Opción 1
Completamente inserta la pausa:
while( isValidState() ) {
execute();
sleep();
}
Opcion 2
Sobrescribir 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
incorporado. El método isValidState
solo debe determinar si el ciclo debe continuar. El caballo de batalla del método debe abstraerse en el método de execute
, que permite que las subclases anulen el comportamiento predeterminado (una tarea difícil usando un switch
integrado y goto
).
Ejemplo de Python
Contraste la siguiente respuesta (a una pregunta de Python) que se publicó en StackOverflow:
- Bucle para siempre.
- Pídale al usuario que ingrese su elección.
- Si la entrada del usuario es ''reiniciar'', continúe bucleando para siempre.
- De lo contrario, deja de bucle para siempre.
- Fin.
while True:
choice = raw_input(''What do you want? '')
if choice == ''restart'':
continue
else:
break
print ''Break!''
Versus:
- Inicializa la elección del usuario.
- Bucle, mientras que la elección del usuario es la palabra ''reiniciar''.
- Pídale al usuario que ingrese su elección.
- Fin.
choice = ''restart'';
while choice == ''restart'':
choice = raw_input(''What do you want? '')
print ''Break!''
Aquí, while True
da como resultado un código engañoso y demasiado complejo.
Estoy escribiendo un código que se ve así:
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 salir del ciclo colocando un salto condicional justo después del cambio. Solo quiero saber si C ++ tiene alguna construcción para esto.
¿Por qué no solo arreglas la condición en tu ciclo 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**
}
}
AFAIK no hay "doble ruptura" o construcción similar en C ++. Lo más cercano sería un goto
, que, si bien tiene una mala connotación para su nombre, existe en el lenguaje por alguna razón, siempre que se use cuidadosamente y con moderación, es una opción viable.
Creo;
while(msg->state != mExit)
{
switch(msg->state)
{
case MSGTYPE: // ...
break;
case DONE:
// ..
// ..
msg->state =mExit;
break;
}
}
if (msg->state ==mExit)
msg->state =DONE;
Incluso si no le gusta ir, no use una excepción para salir de un bucle. La siguiente muestra muestra lo feo que podría ser:
try {
while ( ... ) {
switch( ... ) {
case ...:
throw 777; // I''m afraid of goto
}
}
}
catch ( int )
{
}
Yo usaría goto
como en this respuesta. En este caso, goto
hará que el código sea más claro que cualquier otra opción. Espero que this pregunta sea útil.
Pero creo que usar goto
es la única opción aquí debido a la cadena while(true)
. Debería considerar refactorizar su ciclo. Supongo que la 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 ...
}
La forma más sencilla de hacerlo es poner un IF simple antes de hacer el SWITCH, y que SI pruebe su condición para salir del bucle .......... tan simple como pueda ser
La palabra clave break
en C ++ solo finaliza la iteración envolvente más anidada o la instrucción switch
. Por lo tanto, no podría salir del ciclo while (true)
directamente dentro de la instrucción switch
; sin embargo, podría usar el siguiente código, que creo que es un patrón excelente 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 ciclo for
; es decir, si actualmente tiene:
while (true) {
switch (msg->state) {
case MSGTYPE:
//...
break;
//...
case DONE:
do_cleanup();
break;
}
if (msg->state == DONE)
break;
msg = next_message();
}
Luego, use en su lugar:
for (; msg->state != DONE; msg = next_message()) {
switch (msg->state) {
case MSGTYPE:
//...
break;
//...
}
}
assert(msg->state == DONE);
do_cleanup();
Me sorprende lo simple que es considerar 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;
}
}
Jaja !! ¡De Verdad! ¡Eso es todo lo que necesitas! Una variable extra!
No hay una construcción C ++ para salir del lazo en este caso.
Utilice una bandera para interrumpir el bucle o (si corresponde) extraiga su código en una función y use return
.
No, C ++ no tiene una construcción para esto, dado que la palabra clave "break" ya está reservada para salir del bloque de conmutadores. Alternativamente, un do..while () con una bandera de salida podría ser suficiente.
do {
switch(option){
case 1: ..; break;
...
case n: .. ;break;
default: flag = false; break;
}
} while(flag);
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())
;
Podrías usar goto, pero preferiría establecer un indicador que detenga el ciclo. Luego salga del interruptor.
Puedes usar goto
.
while ( ... ) {
switch( ... ) {
case ...:
goto exit_loop;
}
}
exit_loop: ;
Si recuerdo bien la sintaxis de C ++, puede agregar una etiqueta para break
declaraciones, al igual que para goto
. Entonces, lo que quieres sería escrito 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
Tengo el mismo problema y lo soluciono 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;
}
Una manera ingeniosa 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 sugirió, podría usar un goto o lanzar una excepción dentro del interruptor.
Una solución alternativa es utilizar la palabra clave continue
en combinación con la 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 utilice la declaración de break
para terminar las etiquetas de casos que deben terminar el ciclo.
Por supuesto, esta solución solo funciona si no hay código adicional para ejecutar después de la instrucción switch.
while(true)
{
switch(x)
{
case 1:
{
break;
}
break;
case 2:
//some code here
break;
default:
//some code here
}
}
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**
}
}