Break y continue para salir de un bucle: un goto encubierto.
Detalles- Detalles
- Categoría: Artículos
- Publicado el Miércoles, 11 Abril 2007 00:00
Algunos lenguajes han heredado del C una curiosa forma de salir de los bucles: las instrucciones break y continue. Entre estos lenguajes se encuentran Java, C#, C++, php, python, Delphi... en fin, un montón.
Utilizar estas instrucciones para salir de un bucle supone, aunque no lo parezca, incluir un salto incondicional en el interior del bucle, es decir, el equivalente a la maltrecha instrucción goto.
Como en todo, hay partidarios y detractores de estas dos instrucciones. En general, no son demasiado problemáticas si no se utilizan indiscriminadamente, ya que su ámbito de aplicación es muy reducido: siempre deben estar en el interior de un bucle.
La instrucción break permite detener incondicionalmente un bucle. Es decir, si se alcanza esta instrucción, se pasa bruscamente a la instrucción siguiente al fin del bucle. Veamos un ejemplo (Como de costumbre, en C#):
1 2 3 4 5 6 7 8 9 10 11 | int i; for (i = 0; i < 10; i++) { Console.WriteLine("valor de i: {0}", i); if (i > 4) { break; } } Console.WriteLine("Fin del bucle"); Console.WriteLine("valor de i: {0}", i); |
El resultado de la ejecución es el siguiente:
valor de i: 0 valor de i: 1 valor de i: 2 valor de i: 3 valor de i: 4 valor de i: 5 Fin del bucle valor de i: 5
Realmente, lo que hemos hecho es saltar a la instrucción siguiente al fin del bucle cuando se alcanza el break. Es decir, como si utilizasemos un goto:
1 2 3 4 5 6 7 8 9 10 11 12 | int i; for (i = 0; i < 10; i++) { Console.WriteLine("valor de i: {0}", i); if (i > 4) { goto salir; } } salir: Console.WriteLine("Fin del bucle"); Console.WriteLine("valor de i: {0}", i); |
¿Sorprendid@? Pues sí... la instrucción goto todavía existe en C#, y en otros muchos lenguajes... otra cosa es que, en general, su utilización sea contraproducente.
Pero volvamos al break. La pregunta del millón es: ¿Debemos utilizar ésta instrucción? No hay una respuesta rotunda. En general, no está bien visto utilizarla como norma, ni en el ámbito académico ni en el profesional. Básicamente, podemos apoyarnos en dos argumentos bastante sólidos.
En primer lugar, y desde un punto de vista meramente teórico, se puede argumentar que la instrucción break supone un salto incondicional, y por lo tanto, aquel algoritmo que la emplea, no sigue la programación estructurada. En el paradigma de la programación estructurada, todo algoritmo propio debe expresarse sólo con instrucciones repetitivas, secuenciales o condicionales. Desde los años 60 o 70, en los que las bondades de la programación estructurada quedaron claramente establecidas, se asume que para demostrar cualquier propiedad acerca de un algoritmo (por ejemplo, que haga lo que se le pide, o simplemente que funcione siempre), el algoritmo propuesto debe ser propio y estructurado. El teorema de Böhm-Jacopini nos garantiza que todo algoritmo propio puede expresarse con programación estructurada. Así pues ¿Para qué utilizar saltos incodicionales?
En segundo lugar, en un ámbito puramente práctico, es necesario tener en cuenta que los programas que desarrollemos no terminan su ciclo de vida cuando se ponen en producción: sufren cambios, modificaciones, ampliaciones... El hecho de salir de un bucle saltándose a la torera la condición del bucle provoca que cualquier programador que se enfrente a un código que no es suyo y que contenga saltos incodicionales (o incluso el mismo programador un tiempo después, cuando ya no tiene "fresco" el programa en la memoria) deba realizar un esfuerzo adicional por entender el código, ya que todos esperamos una salida natural del bucle cuando su condición no se cumple... y por supuesto, es muy complicado demostrar que el algoritmo o una modificación del algoritmo funciona. Así pues ¿por qué hacer nuestros algorimos más difíciles de entender? ¿Por qué complicar la demostración de que nuestros algoritmos son correctos?
Una instrucción break puede ser evitada siempre. Sin excepciones. Para ello, suele ser necesario ampliar la condición del bucle, contemplando la condición que hacía que se ejecutase la instrucción break.
En cuanto a la instrucción continue, el razonamiento es análogo a break. La instrucción continue hace que se salte al final del bucle, justo antes de repetir. No deja de ser un salto incondicional.
Por ejemplo, éste código
1 2 3 4 5 6 7 8 9 10 11 | int i; for (i = 0; i < 10; i++) { if ((i >= 4) && (i <=7 )) { continue; } Console.WriteLine("valor de i: {0}", i); } Console.WriteLine("Fin del bucle"); Console.WriteLine("valor de i: {0}", i); |
que produce ésta salida:
valor de i: 0 valor de i: 1 valor de i: 2 valor de i: 3 valor de i: 8 valor de i: 9 Fin del bucle valor de i: 10
es en realidad equivalente a
1 2 3 4 5 6 7 8 9 10 11 12 | int i; for (i = 0; i < 10; i++) { if ((i >= 4) && (i <=7 )) { goto continuar; } Console.WriteLine("valor de i: {0}", i); continuar: } Console.WriteLine("Fin del bucle"); Console.WriteLine("valor de i: {0}", i); |
Al igual que break, la instrucción continue puede evitarse siempre. Los motivos para no utilizarla son básicamente los mismos. Pero a diferencia de break, para evitar la instrucción continue, no suele ser necesario alterar la condición del bucle, basta con incluir algún tipo de condición en el interior del cuerpo del bucle.
En conclusión, desconozco los motivos por los cuales estas dos instrucciones (junto con el goto) todavía siguen existiendo en los lenguajes de programación de alto nivel. En general, suelen crear más problemas que beneficios. Quizá en un momento del pasado pudieron tener alguna importancia, cuando el hardware era caro, los procesadores lentos, los compiladores torpes y los programadores no estaban preparados. Quizá en ese tiempo, arañar unos microsegundos de ejecución utilizando un salto incondicional pudiera suponer alguna ventaja. En un entorno como el actual con procesadores superescalares y superpipeline, compiladores optimizadores, treinta años de algoritmia detrás y programadores especializados han perdido el sentido que pudieran tener tiempo atrás. Las optimizaciones, cuando hablamos de alto nivel es mejor dejárselas al compilador. Recordemos que el último Premio Turing (2006) ha recaido en Fran Allen, por su trabajo en este campo.

