Premisa
El siguiente código debe considerarse de mala forma, independientemente del idioma o la funcionalidad deseada:
while( true ) {
}
Argumentos de apoyo
El while( true )
bucle es de mala forma porque:
- Rompe el contrato implícito de un bucle while.
- La declaración del ciclo while debe indicar explícitamente la única condición de salida.
- Implica que se repite para siempre.
- Se debe leer el código dentro del bucle 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 ineficaz.
- Hay varias condiciones de terminación de bucle, incluida la comprobación de "verdadero".
- Es propenso a los 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.
- Análisis automático de código fuente.
- Para encontrar errores, análisis de complejidad del programa, verificaciones de seguridad o derivar automáticamente cualquier otro comportamiento del código fuente sin la ejecución del código, especificar las condiciones iniciales de ruptura permite que los algoritmos determinen invariantes útiles, mejorando así las métricas de análisis automático del código fuente.
- Bucles infinitos.
- Si todo el mundo siempre usa
while(true)
bucles for que no son infinitos, perdemos la capacidad de comunicarnos de manera concisa cuando los bucles en realidad no tienen una condición de terminación. (Podría decirse que esto ya sucedió, por lo que el punto es discutible).
Alternativa a "Ir a"
El siguiente código es una mejor forma:
while( isValidState() ) {
execute();
}
bool isValidState() {
return msg->state != DONE;
}
Ventajas
Sin bandera. No se 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 ciclo del propio ciclo.
- Permite a alguien que mantiene el código ampliar fácilmente la funcionalidad.
- Permite que se asignen múltiples condiciones de terminación en un solo lugar.
- Separa la cláusula de terminación del código a ejecutar.
- Es más seguro para las centrales nucleares. ;-)
El segundo punto es importante. Sin saber cómo funciona el código, si alguien me pide que haga que el bucle principal deje que otros subprocesos (o procesos) tengan algo de tiempo de CPU, se me ocurren dos soluciones:
Opción 1
Inserte fácilmente la pausa:
while( isValidState() ) {
execute();
sleep();
}
Opcion 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 archivo switch
. El isValidState
método solo debe determinar si el ciclo debe continuar. El caballo de batalla del método debe abstraerse en el execute
método, lo que permite que las subclases anulen el comportamiento predeterminado (una tarea difícil con un switch
y incorporado goto
).
Ejemplo de Python
Compare 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úa repitiendo para siempre.
- De lo contrario, deje de hacer bucles para siempre.
- Final.
Código
while True:
choice = raw_input('What do you want? ')
if choice == 'restart':
continue
else:
break
print 'Break!'
Versus:
- Inicialice la elección del usuario.
- Bucle mientras la elección del usuario es la palabra 'reiniciar'.
- Pídale al usuario que ingrese su elección.
- Final.
Código
choice = 'restart';
while choice == 'restart':
choice = raw_input('What do you want? ')
print 'Break!'
Aquí, while True
resulta en un código engañoso y demasiado complejo.