¿Qué queda de la Programación Estructurada?
Como muchos programadores de C++, he comprobado que las salidas a mitad de bucle con BREAK, o a mitad de función con RETURN, pueden simplificar el código de un programa. Incluso un comando GO TO puede acelerar un algoritmo sin romper una estructura que se puede calificar como “adecuada”. De hecho, las excepciones, que simplifican tanto el tratamiento de errores, se pueden ver como instrucciones GO TO enmascaradas. Pero entonces, los lenguajes actuales, como Java, que fomentan el uso de BREAK, los RETURN a mitad de función y las excepciones, ¿no socavan los cimientos de la Programación Estructurada? La pregunta solo se puede responder si se profundiza en lo que significa la Programación Estructurada. Pero para ello primero voy a retroceder un poco en el tiempo... hasta 1985.
Aquel año me regalaron mi primer ordenador, un Commodore 64. Sus 16 KB de ROM almacenaban un interprete del lenguaje de programación Basic que hacía las veces de sistema operativo. Ahora veo que era un lenguaje tosco, con las líneas del programa numeradas en una única secuencia, sin subrutinas etiquetadas, y con solo unas decenas de instrucciones. Sin embargo, en aquel momento, aquello era maravilloso, y en unos meses fui capaz de concatenar algunos cientos de líneas para construir juegos, único objetivo de mi vida en aquellos días. Desgraciadamente mis juegos eran primitivos, pues cuando el número de líneas aumentaba y llegaba al millar, la complejidad del código me hacía desesperar y, lo peor, hacía naufragar todos mis programas.
Como pensaba que todo se debía a la falta de orden, me dedicaba a reordenar el código y a poner comentarios. En realidad el problema se derivaba de que no había una estructura subyacente al programa, y esto hacia que el más mínimo cambio fuese muy complejo de realizar. Así por ejemplo, la línea 100 podía contener un cálculo que dependía de una variable que se inició en la línea 50. Sin embargo nada, aparte de mi memoria, me impedía saltar, con un comando GO TO, a cualquier línea entre la 51 y la 99 sin pasar por la inicialización de la línea 50 para ejecutar el cálculo de la línea 100. Un olvido en la codificación y la ejecución resultaba en desastre. Mi código era un ejemplo perfecto de lo que se conocía como “código espagueti”.
Años más tarde, en una asignatura de 2º de Informática llamada Estructuras de Programas y Datos, aprendí un lenguaje llamado MODULA-2, en el que era imposible producir código espagueti. En aquella asignatura se nos habló de las virtudes de la Programación Estructurada, de la Programación Modular y de su última consecuencia la Programación Orientada a Objetos. Además se demonizó el uso de todo aquello que oscurecía el flujo de ejecución, como los comandos BREAK en los bucles, los RETURN a mitad de función o las temibles instrucciones GO TO. Sin embargo no hubo en clase una definición formal de Programación Estructurada, para encontrarla debemos retroceder otra vez en el tiempo.
En la década de 1960 E. W. Dijkstra, junto con otros investigadores, buscaban una solución a una crisis del software que estaba conduciendo al fracaso de la mayoría de los proyectos informáticos que en aquella época se abordaban. El problema era el mismo que me afectó con mi Commodore 64 en 1985: la falta de estructura de los programas. Dijkstra, en vez de desesperarse como hice yo, estudió los diferentes patrones que se usaban al crear los programas y llego a la conclusión de que con solo 3 estructuras básicas se podía construir cualquier tipo de programa [1]. Estas estructuras eran: la secuencia de instrucciones, la selección (IF-THEN) y la iteración (WHILE).
Se comprobó que si solo se usaban estas tres estructuras, y sus combinaciones anidadas, los programas se tornaban más comprensibles, debido a que la secuencia de ejecución tenía una complejidad muy limitada. De hecho es fácil comprobar que si solo se usan estas 3 estructuras todos los bloques de código tienen un único punto de entrada, siendo esta característica fundamental para asegurar la coherencia de los cómputos realizados en cualquier bloque de código en el que no aparezcan variables globales.
Por otro lado se definió el concepto de función con posibilidad de anidamiento como una secuencia delimitada y etiquetada de instancias de las tres estructuras básicas, más la invocación a otras funciones o a ella misma. Estas funciones tendrían como entradas un conjunto de variables de un dominio definido, y proporcionarían como salida otro conjunto de variables también de un dominio definido. La unión de las 3 estructuras básicas más el concepto de función dio lugar al concepto de Codificación Estructurada.
Sin embargo no existía un consenso general sobre lo que era la Programación Estructurada [2]. Primero se condenó al GO TO [3], luego se vio que en realidad no era el GO TO si no la forma de usarlo [4] (al fin y al cabo, el código máquina que genera una Codificación Estructurada debe ser estructurado y sin embargo tiene instrucciones GO TO). Luego se comprobó [5] que la salida de bucles mediante BREAK y de funciones mediante RETURN era simplificadora en muchos casos. Por ello a los 3 patrones básicos se les añadieron 4 patrones más, que en realidad eran extensiones de esos 3 básicos [6]. Estos patrones fueron: el patrón CASE, el patrón DO-UNTIL, el patrón WHILE-REPEAT-UNTIL y el LOOP-BREAKIF-ENDDLOOP. Y así se continuó con discusiones en un sentido y otro hasta que la comunidad se aburrió del tema.
Por fijar una de las últimas posturas podemos citar un artículo de 1978 [7] donde se definían las características de la programación estructurada como:
1) Se permite el uso de los 3 patrones básicos y de los 4 patrones extendidos y se prohíbe el uso de otros patrones.
2) Se fomenta el uso de funciones, en relación jerárquica en forma de árbol entre ellas y agrupadas en módulos de tamaño reducido.
3) Se requiere documentación a nivel de código y de función.
4) Se prohíbe el uso de estructuras no aprobadas, y también se prohíbe la ausencia o falta de claridad en la documentación.
Se aprecia que esta definición se queda corta en numerosos aspectos como las salidas de función a mitad de ejecución, las estructuras de datos o la programación concurrente. Además hay temas que en aquella época no se prevían como las excepciones o la programación orientada a aspectos. Desgraciadamente aun hoy no hay acuerdo general respecto a estos temas [8] y por ello el concepto de Programación Estructurada permanece indefinido. Quizás por eso, mi profesor de Estructuras de Programas y Datos paso de puntillas por tan escabroso tema.
Quizás la única característica que se ha mantenido en todos los lenguajes de programación desde la irrupción de la Programación Estructurada ha sido la imposibilidad de tener más de un punto de entrada para cualquier bloque de código.
Podemos concluir que, ante la pregunta de si todos los elementos incluidos en los modernos lenguajes de programación (como las excepciones, los comandos BREAK, los RETURN a mitad de función, etc.) socavan los cimiento de la Programación Estructurada, la respuesta es que la pregunta no tiene sentido porque la Programación Estructurada carece de definición.
Bibliografía
[1] Dijstra, E. W. “Structured programming” Software Engineering Concepts and Techniques, pags 222-226, 1976.
[2] Dening, Peter J. “Is 'structured programing' any longer the right term?” SIGPLAN Notices, Volumen 9, Numero 11, pags 4-6, Nov 1974.
[3] Naur, Peter “GO TO staments and good ALGOL style”, BIT, Vl¡olumen 3, oags 204.205, 1963.
[4] Dijstra, E. W. “GO-TO considereed harmful”, Communications of the ACM, Volumen 11, Número 3, pags 147-148, Marzo 1968.
[5] Sheppard, Sylvia B. y otros, “Modern Coding Practices and Programmer Performance”, COMPUTER, pags 41-49, Diciembre 1979.
[6] Baker, F. T. “Structured programing in a production programming environment”, Software Engineering, Volumen SE-1, Número 2, pags 241-252, Junio 1975.
[7] Chapin, Ned y Denniston Susan P. “Characteristics of a structured program”, 1978.
[8] Roberts, Eric S. “Loop existis and structured programming: Reopenung the debate”, SIGCSE'95, pags 268-272, 1995.