Mi jefe sigue mencionando con indiferencia que los malos programadores usan break
y continue
en bucles.
Los uso todo el tiempo porque tienen sentido; déjame mostrarte la inspiración:
function verify(object) {
if (object->value < 0) return false;
if (object->value > object->max_value) return false;
if (object->name == "") return false;
...
}
El punto aquí es que primero la función verifica que las condiciones sean correctas, luego ejecuta la funcionalidad real. OMI mismo se aplica con bucles:
while (primary_condition) {
if (loop_count > 1000) break;
if (time_exect > 3600) break;
if (this->data == "undefined") continue;
if (this->skip == true) continue;
...
}
Creo que esto facilita la lectura y depuración; pero tampoco veo un inconveniente.
goto
) son útiles en algunos casos.Respuestas:
Cuando se usan al comienzo de un bloque, cuando se realizan las primeras verificaciones, actúan como condiciones previas, por lo que es bueno.
Cuando se usan en el medio del bloque, con algo de código, actúan como trampas ocultas, por lo que es malo.
fuente
Podrías leer el documento de 1974 de Donald Knuth, Estructuración de la programación, con Ir a declaraciones , en el que analiza varios usos de los
go to
que son estructuralmente deseables. Incluyen el equivalente debreak
ycontinue
declaraciones (muchos de los usos dego to
allí se han desarrollado en construcciones más limitadas). ¿Es tu jefe el tipo de llamar a Knuth un mal programador?(Los ejemplos dados me interesan. Por lo general,
break
ycontinue
no les gustan las personas que les gusta una entrada y una salida de cualquier pieza de código, y ese tipo de persona también frunce el ceño en variosreturn
estados.)fuente
Break
,Continue
yExit
como herramientas en mi caja de herramientas; Los uso donde hace que el código sea más fácil de seguir, y no los uso donde sea más difícil de leer.No creo que sean malos. La idea de que son malas proviene de los días de la programación estructurada. Está relacionado con la noción de que una función debe tener un único punto de entrada y un único punto de salida, es decir, solo uno
return
por función.Esto tiene sentido si su función es larga y si tiene múltiples bucles anidados. Sin embargo, sus funciones deben ser cortas y debe envolver los bucles y sus cuerpos en funciones cortas propias. En general, obligar a una función a tener un único punto de salida puede resultar en una lógica muy complicada.
Si su función es muy corta, si tiene un solo bucle, o en el peor de los casos, dos bucles anidados, y si el cuerpo del bucle es muy corto, entonces está muy claro lo que hace a
break
o acontinue
. También está claro loreturn
que hacen las declaraciones múltiples .Estos problemas se abordan en "Clean Code" de Robert C. Martin y en "Refactoring" de Martin Fowler.
fuente
CALL P(X, Y, &10)
y, en caso de error, la función podría pasar el control a esa declaración, en lugar de regresar al punto de la llamada.Los malos programadores hablan en absoluto (al igual que Sith). Los buenos programadores usan la solución más clara posible ( todas las demás cosas son iguales ).
Usar break y continue frecuentemente hace que el código sea difícil de seguir. Pero si reemplazarlos hace que el código sea aún más difícil de seguir, entonces ese es un mal cambio.
El ejemplo que diste es definitivamente una situación en la que los descansos y continuos deberían ser reemplazados por algo más elegante.
fuente
La mayoría de la gente piensa que es una mala idea porque el comportamiento no es fácilmente predecible. Si está leyendo el código y ve
while(x < 1000){}
que asume que se ejecutará hasta x> = 1000 ... Pero si hay interrupciones en el medio, entonces eso no es cierto, por lo que realmente no puede confiar en su bucle ...Es la misma razón por la que a la gente no le gusta GOTO: claro, se puede usar bien, pero también puede conducir a un codicioso código de espagueti, donde el código salta al azar de una sección a otra.
Para mí, si iba a hacer un bucle que se rompió en más de una condición,
while(x){}
entonces cambiaría a X a falso cuando necesitaba salir. El resultado final sería el mismo, y cualquiera que lea el código sabría mirar más de cerca las cosas que cambiaron el valor de X.fuente
while(notDone){ }
enfoque.break;
conx=false;
no hace que su código sea más claro. Todavía tiene que buscar en el cuerpo esa declaración. Y en el caso dex=false;
que tenga que comprobar que no golpeex=true;
más abajo.while (x < 1000)
, supongo que se ejecutará 1000 veces". Bueno, hay muchas razones por las que eso es falso, incluso six
inicialmente es cero. Por ejemplo, ¿quién dice quex
se incrementa con precisión una vez durante el ciclo y nunca se modifica de otra manera? Incluso para su propia suposición, el hecho de que algo se establezcax >= 1000
no significa que el ciclo finalizará; puede volver a establecerse dentro del rango antes de que se verifique la condición.Sí, puede [re] escribir programas sin declaraciones de interrupción (o retornos desde la mitad de los bucles, que hacen lo mismo). Pero es posible que deba introducir variables adicionales y / o duplicación de código, lo que generalmente hace que el programa sea más difícil de entender. Pascal (el lenguaje de programación) fue muy malo, especialmente para los programadores principiantes por esa razón. Su jefe básicamente quiere que programe en las estructuras de control de Pascal. Si Linus Torvalds estuviera en tu lugar, ¡probablemente le mostraría a tu jefe el dedo medio!
Hay un resultado informático llamado jerarquía de estructuras de control de Kosaraju, que se remonta a 1973 y que se menciona en el famoso (más) documento de Knuth sobre gotos de 1974. (David Thornley ya recomendó este documento de Knuth anteriormente) .) Lo que S. Rao Kosaraju demostró en 1973 es que no es posible reescribir todos los programas que tienen saltos de profundidad de niveles múltiples n en programas con profundidad de descanso menor que n sin introducir variables adicionales. Pero digamos que es solo un resultado puramente teórico. (¡¿Solo agregue algunas variables adicionales?! Seguramente puede hacer eso para complacer a su jefe ...)
Lo que es mucho más importante desde una perspectiva de ingeniería de software es un artículo más reciente de 1995 de Eric S. Roberts titulado Salidas de bucle y programación estructurada: reapertura del debate ( http://cs.stanford.edu/people/eroberts/papers/SIGCSE- 1995 / LoopExits.pdf ). Roberts resume varios estudios empíricos realizados por otros antes que él. Por ejemplo, cuando a un grupo de estudiantes de tipo CS101 se les pidió que escribieran código para una función que implementa una búsqueda secuencial en una matriz, el autor del estudio dijo lo siguiente sobre aquellos estudiantes que usaron un descanso / retorno / goto para salir de el ciclo de búsqueda secuencial cuando se encontró el elemento:
Roberts también dice que:
Sí, es posible que tenga más experiencia que los estudiantes de CS101, pero sin usar la declaración de interrupción (o equivalentemente regresar / ir desde el medio de los bucles), eventualmente escribirá código que, si bien nominalmente está bien estructurado, es bastante complicado en términos de lógica adicional variables y duplicación de código que alguien, probablemente usted mismo, pondrá errores lógicos al tratar de seguir el estilo de codificación de su jefe.
También voy a decir aquí que el trabajo de Roberts es mucho más accesible para el programador promedio, por lo que es mejor leerlo primero que el de Knuth. También es más corto y cubre un tema más específico. Probablemente incluso podría recomendarlo a su jefe, incluso si él es el administrador en lugar del tipo CS.
fuente
No considero usar ninguna de estas malas prácticas, pero usarlas demasiado dentro del mismo ciclo debería justificar repensar la lógica que se usa en el ciclo. Úsalos con moderación.
fuente
El ejemplo que diste no necesita descansos ni continúa:
Mi "problema" con las 4 líneas en su ejemplo es que todas están en el mismo nivel pero hacen cosas diferentes: algunas se rompen, algunas continúan ... Debe leer cada línea.
En mi enfoque anidado, cuanto más profundizas, más 'útil' se vuelve el código.
Pero, si en el fondo encuentra una razón para detener el ciclo (que no sea una condición primaria), una interrupción o retorno tendría su uso. Prefiero eso sobre el uso de una bandera adicional que se probará en la condición de nivel superior. El descanso / retorno es más directo; establece mejor la intención que establecer otra variable más.
fuente
<
comparaciones deben ser<=
para que coincida con la solución de los OPLa "maldad" depende de cómo los uses. Normalmente uso interrupciones en construcciones de bucle SOLO cuando me ahorrará ciclos que no se pueden guardar mediante la refactorización de un algoritmo. Por ejemplo, recorrer una colección en busca de un elemento con un valor en una propiedad específica establecida en verdadero. Si todo lo que necesita saber es que uno de los elementos tenía esta propiedad establecida en verdadero, una vez que logre ese resultado, un descanso es bueno para terminar el ciclo adecuadamente.
Si usar un descanso no hará que el código sea específicamente más fácil de leer, más corto de ejecutar o guardar ciclos de procesamiento de manera significativa, entonces es mejor no usarlos. Cuando es posible, tiendo a codificar con el "mínimo común denominador" para asegurarme de que cualquiera que me siga pueda ver fácilmente mi código y descubrir qué está pasando (no siempre tengo éxito en esto). Los descansos reducen eso porque introducen puntos de entrada / salida extraños. Mal uso, pueden comportarse de manera muy parecida a una declaración "goto" fuera de control.
fuente
Absolutamente no ... Sí, el uso de
goto
es malo porque deteriora la estructura de su programa y también es muy difícil entender el flujo de control.Pero el uso de declaraciones como
break
ycontinue
son absolutamente necesarias en estos días y no se consideran como una mala práctica de programación.Y tampoco es tan difícil de entender el flujo de control en uso de
break
ycontinue
. En construcciones comoswitch
labreak
declaración es absolutamente necesaria.fuente
La noción esencial proviene de poder analizar semánticamente su programa. Si tiene una sola entrada y una única salida, la matemática necesaria para denotar posibles estados es considerablemente más fácil que si tiene que gestionar rutas de bifurcación.
En parte, esta dificultad se refleja en ser capaz de razonar conceptualmente sobre su código.
Francamente, tu segundo código no es obvio. ¿Qué está haciendo? ¿Continúa 'continúa' o 'sigue' el ciclo? No tengo idea. Al menos tu primer ejemplo es claro.
fuente
Reemplazaría su segundo fragmento de código con
no por ninguna razón de terquedad: en realidad creo que esto es más fácil de leer y que alguien entienda lo que está sucediendo. En términos generales, las condiciones para sus bucles deben estar contenidas únicamente dentro de esas condiciones de bucle que no están esparcidas por todo el cuerpo. Sin embargo, hay algunas situaciones donde
break
ycontinue
puede ayudar a la legibilidad.break
más de locontinue
que podría agregar: Dfuente
foreach
bucle ya que esto solo repetirá cada elemento de una colección. Unfor
bucle es similar en el sentido de que no debe tener un punto final condicional. Si necesita un punto final condicional, debe usar unwhile
bucle.break
máximo en mi opinión desde el bucle.No estoy de acuerdo con tu jefe. Hay lugares adecuados para
break
ycontinue
para ser utilizados. De hecho, la razón por la que se introdujeron las excepciones y el manejo de excepciones en los lenguajes de programación modernos es que no puede resolver todos los problemas utilizando solostructured techniques
.En una nota al margen , no quiero comenzar una discusión religiosa aquí, pero podría reestructurar su código para que sea aún más legible de esta manera:
En otra nota al margen
Personalmente, no me gusta el uso de
( flag == true )
condicionales porque si la variable ya es booleana, está introduciendo una comparación adicional que debe suceder cuando el valor del booleano tiene la respuesta que desea, a menos que, por supuesto, esté seguro de que su compilador optimice esa comparación extra de distancia.fuente
boolean
es o qué significa la terminología concisa / elegante, entonces quizás sea mejor que contrate a algunos mantenedores más inteligentes ;-)Estoy de acuerdo con tu jefe. Son malos porque producen métodos con alta complejidad ciclomática. Tales métodos son difíciles de leer y difíciles de probar. Afortunadamente hay una solución fácil. Extraiga el cuerpo del bucle en un método separado, donde "continuar" se convierte en "retorno". "Regresar" es mejor porque después de "regresar" se acabó, no hay preocupaciones sobre el estado local.
Para "break", extraiga el ciclo en un método separado, reemplazando "break" por "return".
Si los métodos extraídos requieren una gran cantidad de argumentos, es una indicación para extraer una clase, ya sea recopilarlos en un objeto de contexto.
fuente
Creo que solo es un problema cuando está anidado profundamente dentro de múltiples bucles. Es difícil saber a qué circuito te estás rompiendo. Puede ser difícil seguir una continuación también, pero creo que el verdadero dolor proviene de los descansos: la lógica puede ser difícil de seguir.
fuente
break 2
; para otros, supongo que se usan banderas temporales de boolSiempre que no se usen como goto disfrazados como en el siguiente ejemplo:
Estoy bien con ellos. (Ejemplo visto en el código de producción, meh)
fuente
No me gusta ninguno de estos estilos. Esto es lo que preferiría:
Realmente no me gusta usar
return
para abortar una función. Se siente como un abuso dereturn
.Usar
break
también no siempre es claro para leer.Mejor aún podría ser:
menos anidamiento y las condiciones complejas se refactorizan en variables (en un programa real tendrías que tener mejores nombres, obviamente ...)
(Todo el código anterior es pseudocódigo)
fuente
break
ycontinue
. Terminar anormalmente un ciclo simplemente se siente raro y no me gusta.continue
significa omitir la funcionalidad ir al siguiente ciclo; no "continuar con la ejecución"No. Es una forma de resolver un problema, y hay otras formas de resolverlo.
Muchos lenguajes convencionales actuales (Java, .NET (C # + VB), PHP, escriba el suyo) usan "break" y "continue" para omitir los bucles. Ambas oraciones "estructuradas".
Sin ellos:
Con ellos:
Tenga en cuenta que el código "break" y "continue" es más corto y generalmente convierte "while" en "for" o "foreach".
Ambos casos son una cuestión de estilo de codificación. Prefiero no usarlos , porque el estilo detallado me permite tener más control del código.
De hecho, trabajo en algunos proyectos, donde era obligatorio usar esas oraciones.
Algunos desarrolladores pueden pensar que no son necesariamente, sino hipotéticos, si tuviéramos que eliminarlos, también tenemos que eliminar "while" y "do while" ("repetir hasta", muchachos pascales) ;-)
Conclusión, incluso si prefiero no usarlos, creo que es una opción, no una mala práctica de programación.
fuente
No estoy en contra
continue
y,break
en principio, pero creo que son construcciones de muy bajo nivel que muy a menudo pueden ser reemplazadas por algo aún mejor.Estoy usando C # como ejemplo aquí, considere el caso de querer iterar sobre una colección, pero solo queremos los elementos que cumplan con algún predicado, y no queremos hacer más de un máximo de 100 iteraciones.
Esto se ve RAZONABLEMENTE limpio. No es muy difícil de entender. Sin embargo, creo que ganaría mucho siendo más declarativo. Compárelo con lo siguiente:
Quizás las llamadas Where y Take ni siquiera deberían estar en este método. Tal vez este filtrado debería hacerse ANTES de que la colección se pase a este método. De todos modos, al alejarnos de las cosas de bajo nivel y centrarnos más en la lógica comercial real, se vuelve más claro en lo que REALMENTE estamos interesados. Se hace más fácil separar nuestro código en módulos cohesivos que se adhieren más a las buenas prácticas de diseño y así en.
Las cosas de bajo nivel seguirán existiendo en algunas partes del código, pero queremos ocultar esto tanto como sea posible, porque se necesita energía mental que podríamos utilizar para razonar sobre los problemas comerciales.
fuente
Code Complete tiene una buena sección sobre el uso
goto
y múltiples retornos de la rutina o el ciclo.En general no es una mala práctica.
break
ocontinue
decir exactamente lo que pasa después. Y estoy de acuerdo con esto.Steve McConnell (autor de Code Complete) usa casi los mismos ejemplos que usted para mostrar las ventajas de usar varias
goto
declaraciones.Sin embargo, el uso excesivo
break
ocontinue
podría conducir a un software complejo e imposible de mantener.fuente