La programación estructural es un paradigma de programación basado en la representación de un programa en forma de una estructura de bloques jerárquica . Fue conceptualizado a fines de la década de 1960 y principios de la de 1970 sobre la base del teorema de Boehm-Jacopini , que fundamenta matemáticamente la posibilidad de la organización estructural de programas, y el trabajo de Edsger Dijkstra "Sobre los peligros del operador goto" ( ing. Ir a considerado nocivo ).
De acuerdo con el paradigma, cualquier programa que se construya sin usar la instrucción goto consta de tres estructuras de control básicas: secuencia, rama , bucle ; además, se utilizan subrutinas . Al mismo tiempo, el desarrollo del programa se lleva a cabo paso a paso, utilizando el método "de arriba hacia abajo".
La metodología de programación estructurada apareció como resultado de la creciente complejidad de las tareas resueltas en las computadoras y, en consecuencia, la complejidad del software. En la década de 1970, el volumen y la complejidad de los programas alcanzaron tal nivel que el desarrollo tradicional (no estructurado) de programas ya no satisface las necesidades de la práctica. Los programas se volvieron demasiado complejos para mantenerlos adecuadamente. Por lo tanto, se requirió la sistematización del proceso de desarrollo y la estructura de los programas.
La metodología de desarrollo de software estructural ha sido reconocida como "la formalización más fuerte de los años 70".
Según Bertrand Meyer, “La revolución de la programación iniciada por Dijkstra condujo al movimiento conocido como programación estructurada, que proponía un enfoque sistemático y racional para el diseño de programas. La programación estructurada se ha convertido en la base de todo lo que se hace en la metodología de la programación, incluida la programación de objetos ” [1] .
Inicialmente, la idea de programación estructurada nació en relación con el operador goto y las dudas sobre la idoneidad de su uso. Estas dudas fueron expresadas por primera vez por Heinz Zemanek en una reunión sobre el idioma algol a principios de 1959 en Copenhague. Sin embargo, este discurso no llamó la atención y no tuvo consecuencias. Edsger Dijkstra recuerda: "Hasta cierto punto, me culpo a mí mismo por no haber sido capaz de apreciar el significado de esta idea en ese momento" [2] [3] [4] .
La situación cambió drásticamente diez años después, cuando Dijkstra publicó su famosa carta Ir a la declaración considerada dañina en marzo de 1968 . Este es un documento verdaderamente histórico que ha tenido un impacto significativo en el desarrollo posterior de la programación.
El destino del documento en sí es muy interesante. El hecho es que Dijkstra le dio al artículo un título completamente diferente: “Argumentos en contra de la Declaración GO TO” (Un caso en contra de la Declaración GO TO).
Sin embargo, en el momento de la publicación, sucedió algo incomprensible: por alguna razón, el artículo se convirtió misteriosamente en una "Carta al editor", y el título anterior desapareció misteriosamente. ¿Lo que realmente sucedió? Dijkstra explicó la misteriosa transformación del artículo en una carta solo muchos años después, en 2001, un año antes de su muerte.
Comunicaciones de la ACM publicó mi texto titulado "La declaración GOTO se considera dañina " . En años posteriores, fue citado a menudo. Desafortunadamente, esto fue hecho a menudo por personas que no vieron nada más en él de lo que dice el título. Este título se ha convertido en la piedra angular de mi fama...
¿Cómo sucedió todo esto? Envié un artículo titulado "El caso contra la declaración GO TO". Para acelerar la publicación, el editor convirtió mi artículo en una Carta al Editor. Al mismo tiempo, se le ocurrió un nuevo título para el artículo, que él mismo inventó. El editor fue Niklaus Wirth [5] [6] .
El propósito de la programación estructurada es aumentar la productividad de los programadores, incluso al desarrollar sistemas de software grandes y complejos, reducir la cantidad de errores y simplificar la depuración, modificación y mantenimiento del software.
Este objetivo se fijó en relación con la creciente complejidad de los programas y la incapacidad de los desarrolladores y administradores de grandes proyectos de software para hacer frente a los problemas que surgieron en 1960-1970 en relación con el desarrollo de herramientas de software [7] .
La programación estructurada está diseñada, en particular, para eliminar el desorden y los errores en los programas causados por dificultades en la lectura del código, no sistematizado, inconveniente para la percepción y análisis del código fuente del programa. Tal texto se caracteriza a menudo como " código espagueti ".
El código espagueti es un programa mal diseñado, mal estructurado, confuso y difícil de entender que contiene muchas instrucciones goto (especialmente saltos hacia atrás), excepciones y otras construcciones que degradan la estructura [8] . Uno de los anti -patrones de programación más famosos .
El código de espagueti se llama así porque el flujo del programa es como un plato de espagueti , es decir, tortuoso y enrevesado. A veces llamado "código canguro" debido a las muchas instrucciones de salto .
Hoy en día, el término se aplica no solo a los casos de abuso de goto, sino también a cualquier código "multienlazado" en el que el mismo pequeño fragmento se ejecuta en un gran número de situaciones diferentes y realiza muchas funciones lógicas diferentes [8] .
El código espagueti se puede depurar y ejecutar correctamente y con un alto rendimiento, pero es extremadamente difícil de mantener y desarrollar [8] . Refinar el código espagueti para agregar nuevas funciones a veces conlleva un potencial significativo para introducir nuevos errores. Por esta razón, se vuelve casi inevitable que la refactorización sea la principal cura para los espaguetis.
A partir de la década de 1970, el operador de salto incondicional goto ha estado en el centro de una crítica sistemática y creciente. El uso incorrecto e irreflexivo de la instrucción goto en el código fuente del programa conduce a un " código espagueti " confuso e ilegible . A partir del texto de dicho código, es casi imposible comprender el orden de ejecución y la interdependencia de los fragmentos.
Este punto de vista se reflejó por primera vez en el artículo de Edsger Dijkstra "El operador Ir a se considera dañino" [3] [9] . Dijkstra notó que la calidad del código es inversamente proporcional al número de instrucciones goto que contiene. El artículo obtuvo una amplia publicidad, como resultado de lo cual se revisaron significativamente las opiniones sobre el uso del operador goto. En Notas sobre programación estructurada [10] , Dijkstra argumentó que es mucho más fácil verificar la corrección formal del código sin un goto .
El código con goto es difícil de formatear, ya que puede romper la jerarquía de ejecución (un paradigma de la programación estructurada) y, por lo tanto, es posible que la sangría, diseñada para reflejar la estructura del programa, no siempre se configure correctamente. Además, la instrucción goto evita que los compiladores optimicen las estructuras de control [11] .
Algunos usos de goto pueden crear problemas con la lógica de ejecución del programa:
Los argumentos en contra de la sentencia goto resultaron ser tan serios que en programación estructurada comenzaron a ser considerados como altamente indeseables. Esto se reflejó en el diseño de nuevos lenguajes de programación. Por ejemplo, goto es ilegal en Java y Ruby . En varios idiomas modernos, todavía se deja por razones de eficiencia en aquellos casos raros en los que se justifica el uso de goto. Por ejemplo, goto sobrevivió en Ada , uno de los lenguajes arquitectónicamente más sofisticados de la historia [12] .
Sin embargo, en los lenguajes de alto nivel donde se ha conservado este operador, su uso, por regla general, está sujeto a severas restricciones que impiden el uso de los métodos más peligrosos de su uso: por ejemplo, está prohibido pasar el control. desde fuera un bucle, procedimiento o función dentro. El estándar del lenguaje C++ prohíbe pasar por alto la inicialización de variables con goto.
El teorema fue formulado y probado por los matemáticos italianos Corrado Böhm y Giuseppe Jacopini. Lo publicaron en 1965 en italiano y en 1966 en inglés [13] . Junto con el teorema, el artículo de Boehm y Jacopini describe métodos para convertir algoritmos no estructurales en algoritmos estructurales utilizando como ejemplo el lenguaje de programación P′′ creado por Bohm . El lenguaje P′′ es el primer lenguaje de programación completo de Turing sin el operador goto .
El teorema de Böhm-Jacopini está escrito en un lenguaje complejo y en una notación inusual. Si usamos terminología y notación modernas, tomará la forma:
Cualquier programa dado en forma de diagrama de flujo se puede representar utilizando tres estructuras de control:
donde f, g son diagramas de bloques con una entrada y una salida,
p - condición, THEN, IF, ELSE, WHILE, DO son palabras clave [14] .Explicación. La fórmula f ENTONCES g significa lo siguiente: primero se ejecuta el programa f, luego se ejecuta el programa g.
Como señala Harlan Mills , este teorema contrasta marcadamente con la práctica de programación habitual (en los años 60 y 70), cuando había un uso masivo de los operadores goto jump [14] .
Boehm y Jacopini no utilizaron el término "programación estructurada". Sin embargo, el teorema demostrado por ellos (y sus posteriores variaciones por diferentes autores) pasó posteriormente a denominarse “teorema de programación estructural”, “teorema estructural” [14] , “teorema de estructuración” [15] .
La formación y desarrollo de la programación estructurada está asociada con el nombre de Edsger Dijkstra [10] [16] .
Principio 1. Debe dejar de usar el operador de salto incondicional goto.
Principio 2. Todo programa se construye a partir de tres estructuras básicas de control: secuencia, bifurcación, ciclo.
Principio 3. En un programa, las estructuras básicas de control se pueden anidar entre sí de forma arbitraria. No se proporcionan otros medios para controlar la secuencia de operaciones.
Principio 4. Los fragmentos repetitivos del programa pueden organizarse en forma de subrutinas (procedimientos y funciones ). De la misma manera (en forma de subprogramas) es posible ordenar lógicamente fragmentos integrales del programa, incluso si no se repiten.
Principio 5. Cada grupo de instrucciones lógicamente completo debe organizarse como un bloque . Los bloques son la base de la programación estructurada.
Un bloque es una pieza de código fuente agrupada lógicamente, como un conjunto de instrucciones escritas en una fila en el código fuente de un programa. El concepto de bloque significa que un bloque de instrucciones debe tratarse como una sola instrucción. Los bloques sirven para limitar el alcance de variables y funciones. Los bloques pueden estar vacíos o anidados unos dentro de otros. Los límites de los bloques están estrictamente definidos. Por ejemplo, en una sentencia if, el bloque está delimitado por código BEGIN..END(en Pascal) o llaves {...} (en C) o sangría (en Python).Principio 6. Todas las estructuras enumeradas deben tener una entrada y una salida.
Las estructuras de control arbitrarias (como en un plato de espaguetis) pueden tener un número arbitrario de entradas y salidas. Al limitarnos a estructuras de control con una entrada y una salida, obtenemos la capacidad de construir algoritmos arbitrarios de cualquier complejidad usando mecanismos simples y confiables [17] .Principio 7. El desarrollo del programa se realiza paso a paso, utilizando el método "top-down" (método top-down) [18] .
Primero, se escribe el texto del programa principal, en el que, en lugar de cada fragmento de texto lógico conectado, se inserta una llamada a la subrutina que ejecutará este fragmento. En lugar de subrutinas de trabajo reales, se insertan partes ficticias en el programa: stubs , que, en pocas palabras, no hacen nada.
Para ser más precisos, un stub satisface los requisitos de la interfaz del fragmento (módulo) que se reemplaza, pero no realiza sus funciones o las realiza parcialmente. Luego, los stubs se reemplazan o actualizan a verdaderos fragmentos (módulos) con todas las funciones de acuerdo con el plan de programación. En cada etapa del proceso de implementación, el programa ya creado debe funcionar correctamente en relación con el nivel inferior. El programa resultante se comprueba y se depura [19] .
Una vez que el programador está convencido de que las subrutinas se llaman en la secuencia correcta (es decir, la estructura general del programa es correcta), las rutinas auxiliares se reemplazan secuencialmente por las reales y el desarrollo de cada subrutina se lleva a cabo de la misma manera. manera como el programa principal. El desarrollo termina cuando no quedan stubs.
Tal secuencia asegura que en cada etapa de desarrollo el programador trate simultáneamente con un conjunto de fragmentos visibles y comprensibles, y puede estar seguro de que la estructura general de todos los niveles superiores del programa es correcta.
Al mantener y realizar cambios en el programa, resulta qué procedimientos deben cambiarse. Se introducen sin afectar partes del programa que no están directamente relacionadas con ellos. Esto asegura que al hacer cambios y corregir errores, alguna parte del programa que actualmente está fuera del área de atención del programador no fallará [18] [20] [21] [22] [23] [24 ] .
También se debe tener en cuenta que en el "Prólogo" del libro "Programación estructurada" [25] , Tony Hoare señala que los principios de la programación estructurada se pueden aplicar igualmente al desarrollo de programas tanto "de arriba hacia abajo" como "de abajo hacia arriba". [26] .
Las subrutinas no eran una condición necesaria para la posibilidad de implementar la programación estructurada [27] . Inicialmente, las subrutinas aparecieron como un medio para optimizar los programas en términos de la cantidad de memoria ocupada: permitían no repetir bloques de código idénticos en el programa, sino describirlos una vez y llamarlos según fuera necesario. Por ahora[ ¿cuándo? ] esta función de las subrutinas se ha convertido en auxiliar, su objetivo principal es la estructuración del programa para que sea más fácil de entender y mantener.
Separar un conjunto de acciones en una subrutina y llamarla según sea necesario le permite seleccionar lógicamente una subtarea integral que tiene una solución típica. Tal acción tiene una ventaja más (además de ahorrar memoria) sobre repetir el mismo tipo de acciones. Cualquier cambio (corrección de errores, optimización, extensión de funcionalidad) realizado en la subrutina se refleja automáticamente en todas sus llamadas, mientras que en la duplicación, cada cambio debe realizarse en cada aparición del código que se modifica.
Incluso en aquellos casos en que se asigna un conjunto de acciones de una sola vez a la subrutina, esto está justificado, ya que permite reducir el tamaño de los bloques integrales de código que componen el programa, es decir, hacer que el programa sea más comprensible. y visibles
Seguir los principios de la programación estructurada hizo que los textos de los programas, incluso los bastante grandes, fueran normalmente legibles. La comprensión de los programas se ha vuelto mucho más fácil, se ha vuelto posible desarrollar programas en un modo industrial normal, cuando un programa puede ser entendido sin mucha dificultad no solo por su autor, sino también por otros programadores. Esto hizo posible desarrollar sistemas de software que eran bastante grandes para ese momento por las fuerzas de los equipos de desarrollo y mantener estos complejos durante muchos años, incluso frente a cambios inevitables en la composición del personal.
La programación estructurada mejora en gran medida la claridad y la legibilidad de los programas [28] . Edward Jordan explica:
El comportamiento de muchos programas no estructurales suele estar más cerca del movimiento browniano que de cualquier proceso organizado. Cualquier intento de leer la lista lleva a una persona a la desesperación porque dicho programa normalmente ejecuta varias declaraciones, después de lo cual el control se transfiere a un punto que se encuentra unas pocas páginas más abajo. Se ejecutan algunas declaraciones más allí y el control se transfiere nuevamente a algún punto aleatorio. Aquí se ejecutan algunos operadores más, etc. Después de varias transmisiones de este tipo, el lector olvida cómo empezó todo. Y pierde el hilo de sus pensamientos.
Los programas estructurales, por otro lado, tienden a organizarse y ejecutarse secuencialmente [29] .
La mejora en la legibilidad de los programas estructurados se debe a que la ausencia de la instrucción goto permite que el programa se lea de arriba a abajo sin interrupciones causadas por transferencias de control. Como resultado, puede descubrir inmediatamente (de un vistazo) las condiciones necesarias para modificar uno u otro fragmento del programa [30] .
La tecnología P para la producción de programas, o “tecnología de programación bidimensional” [31], fue creada en el Instituto de Cibernética V. M. Glushkov [32] . El sistema gráfico de la tecnología de programación R está consagrado en los estándares GOST 19.005-85 [33] , GOST R ISO/IEC 8631-94 [34] y el estándar internacional ISO 8631Н.
El autor de la tecnología de programación R, Doctor en Ciencias Físicas y Matemáticas, el profesor Igor Velbitsky, propuso reconsiderar el concepto mismo de "estructura del programa". Según él, “la estructura es un concepto multidimensional. Por lo tanto, la visualización de este concepto con la ayuda de textos lineales (secuencias de operadores) reduce a casi nada las ventajas del enfoque estructural. Las enormes posibilidades asociativas del aparato visual y del aparato del pensamiento humano se utilizan prácticamente en vano - para el reconocimiento de imágenes estructurales en forma de una secuencia uniforme de símbolos" [35] .
La metodología de la programación estructurada bidimensional difiere significativamente de la programación estructurada clásica unidimensional (textual) [36] [37] .
Las ideas de la programación estructurada se desarrollaron cuando los gráficos por computadora aún no existían y la herramienta principal para el algoritmo y el programador era el texto unidimensional (lineal o escalonado ). Antes del advenimiento de los gráficos por computadora, la metodología de la programación estructurada clásica era la mejor solución [10] .
Con la llegada de los gráficos por computadora, la situación ha cambiado. Utilizando los medios expresivos de los gráficos, se hizo posible modificar, desarrollar y complementar tres tipos de estructuras estructurales de control básicas (de texto), así como abandonar por completo las palabras clave if , then, else, case , switch, break, while , do, repeat, before, for, foreach, continue, loop, exit, when, last, etc. y reemplácelos con gráficos de control, es decir, use programación estructurada bidimensional [33] [36] .
![]() |
|
---|