Un bucle es una especie de estructura de control en lenguajes de programación de alto nivel , diseñada para organizar la ejecución repetida de un conjunto de instrucciones . Además, un ciclo se puede llamar cualquier secuencia de instrucciones ejecutada repetidamente, organizada de cualquier manera (por ejemplo, usando un salto condicional ).
Una secuencia de instrucciones destinada a ejecutarse repetidamente se denomina cuerpo de bucle . Una única ejecución del cuerpo del bucle se denomina iteración . La expresión que determina si la iteración se realizará de nuevo o si el ciclo terminará se denomina condición de salida o condición de fin del ciclo (o condición de continuación , según se interprete su verdad, como una señal de la necesidad de terminar). o continuar el bucle). La variable que almacena el número de iteraciones actual se denomina contador de iteraciones de bucle o simplemente contador de bucles . El bucle no contiene necesariamente un contador, el contador no tiene por qué serlo; la condición para salir del bucle puede depender de varias variables modificadas en el bucle, o puede estar determinada por condiciones externas (por ejemplo, el inicio de un determinado tiempo), en este último caso, el contador puede no ser necesario en absoluto.
La ejecución de cualquier bucle incluye la inicialización inicial de las variables del bucle, la comprobación de la condición de salida, la ejecución del cuerpo del bucle y la actualización de la variable del bucle en cada iteración. Además, la mayoría de los lenguajes de programación proporcionan medios para el control anticipado del ciclo, por ejemplo, declaraciones de finalización del ciclo, es decir, salida del ciclo independientemente de la verdad de la condición de salida (en lenguaje C - break) y operadores de salto de iteración ( en lenguaje C - continue).
A veces, los programas usan bucles, cuya salida no es proporcionada por la lógica del programa. Tales ciclos se llaman incondicionales o infinitos. Debido a su atipicidad, los lenguajes de programación no proporcionan medios sintácticos especiales para crear bucles infinitos, por lo que dichos bucles se crean utilizando construcciones diseñadas para crear bucles ordinarios (o condicionales ). Para garantizar una repetición infinita, la verificación de condición en un bucle de este tipo está ausente (si la sintaxis lo permite, como, por ejemplo, en el bucle del LOOP ... END LOOPlenguaje Ada ), o se reemplaza por un valor constante ( while true do ...en Pascal ). El lenguaje C usa un bucle for(;;)con secciones vacías o un bucle while (1).
Un bucle con una condición previa es un bucle que se ejecuta mientras alguna condición especificada antes de su inicio es verdadera. Esta condición se verifica antes de la ejecución del cuerpo del ciclo, por lo que el cuerpo no se puede ejecutar ni una sola vez (si la condición es falsa desde el principio). En la mayoría de los lenguajes de programación de procedimientos, se implementa mediante la instrucción while , por lo que su segundo nombre es el ciclo while. En Pascal, un ciclo con una condición previa se ve así:
while < condición > do begin < cuerpo del bucle > end ;En lenguaje C :
while ( < condición > ) { < cuerpo del bucle > }Un bucle con una condición posterior es un bucle en el que la condición se comprueba después de la ejecución del cuerpo del bucle. De ello se deduce que el cuerpo siempre se ejecuta al menos una vez. En Pascal, este bucle lo implementa el operador repeat..until ; en C- do…while.
En Pascal, un ciclo con una condición posterior se ve así:
repetir < cuerpo del bucle > hasta < condición de salida >En lenguaje C:
hacer { < cuerpo del bucle > } while ( < condición de continuación del bucle > )Existen diferencias en la interpretación de la condición de bucle con una condición posterior en diferentes idiomas. En Pascal y los idiomas que descienden de él, la condición de dicho ciclo se trata como una condición de salida (el ciclo termina cuando la condición es verdadera, en la terminología rusa, estos ciclos también se denominan "ciclo hasta"), y en C y sus descendientes, como una condición de continuación (el ciclo termina cuando la condición es falsa; estos bucles a veces se denominan bucles while).
Un ciclo de salida intermedia es la forma más común de un ciclo condicional. Sintácticamente, dicho ciclo se forma utilizando tres construcciones: el comienzo del ciclo, el final del ciclo y el comando para salir del ciclo. La construcción inicial marca el punto del programa en el que comienza el cuerpo del ciclo, la construcción final marca el punto donde termina el cuerpo. Dentro del cuerpo, debe haber un comando de salida del bucle, tras la ejecución del cual el bucle finaliza y el control se transfiere al operador siguiendo la construcción del final del bucle. Naturalmente, para que el ciclo se ejecute más de una vez, el comando de salida no debe llamarse incondicionalmente, sino solo cuando se cumpla la condición para salir del ciclo.
La diferencia fundamental entre este tipo de bucle y los considerados anteriormente es que la parte del cuerpo del bucle situada después del inicio del bucle y antes del comando de salida siempre se ejecuta (incluso si la condición de salida del bucle es verdadera en la primera iteración). ), y la parte del cuerpo del ciclo ubicada después del comando de salida, no se ejecuta en la última iteración.
Es fácil ver que con un ciclo de salida central, puede modelar fácilmente tanto un ciclo de precondición (colocando la declaración de salida al principio del cuerpo del ciclo) como un ciclo de poscondición (colocando la declaración de salida al final del ciclo). cuerpo).
Algunos lenguajes de programación contienen construcciones especiales para organizar un ciclo con una salida desde el medio. Entonces, en el lenguaje de Ada , la construcción LOOP ... END LOOPy el comando de salida se usan para esto, EXITo EXIT WHEN:
LOOP ... Parte del cuerpo del bucle EXIT WHEN < condición de salida > ; ... Parte del cuerpo del bucle IF < condición de salida > THEN EXIT ; FIN ; ... Parte del cuerpo del END LOOP :Aquí, dentro del bucle, puede haber cualquier número de comandos de salida de ambos tipos. Los comandos de salida en sí mismos no difieren fundamentalmente, generalmente se EXIT WHENusan cuando solo se verifica la condición de salida, pero simplemente EXIT cuando se sale del ciclo en una de las variantes de una declaración condicional compleja.
En los lenguajes donde no se proporcionan tales construcciones, se puede modelar un ciclo con salida desde el medio usando cualquier ciclo condicional y un operador de salida anticipada del ciclo (como breaken C, salir en Turbo Pascal, etc.), o un transición de operador incondicional goto .
Un bucle con contador es un bucle en el que alguna variable cambia su valor de un valor inicial dado a un valor final con algún paso, y para cada valor de esta variable, el cuerpo del bucle se ejecuta una vez. En la mayoría de los lenguajes de programación de procedimientos, se implementa mediante una declaración forque especifica el contador (la llamada "variable de bucle"), el número requerido de pases (o el valor límite del contador) y posiblemente el paso en el que el contador. cambios. Por ejemplo, en el lenguaje Oberon-2 , dicho ciclo se ve así:
POR v := b A e POR s HACER ... cuerpo de bucle FINALaquí v es el contador, b es el valor inicial del contador, e es el valor límite del contador, s es el paso).
La pregunta sobre el valor de una variable al final de un bucle en el que esta variable se utilizó como contador es ambigua. Por ejemplo, si un programa Pascal encuentra una construcción de la forma:
yo := 100 ; for i := 0 to 9 do begin ... loop body end ; k := yo ;surge la pregunta: ¿qué valor se le asignará finalmente a la variable k : 9, 10, 100, tal vez algún otro? ¿Qué pasa si el ciclo termina prematuramente? Las respuestas dependen de si el valor del contador se incrementa después de la última iteración y si el traductor cambia este valor adicionalmente. Una pregunta más: ¿qué sucederá si al contador se le asigna explícitamente un nuevo valor dentro del bucle? Diferentes lenguajes de programación se ocupan de estos problemas de diferentes maneras. En algunos, el comportamiento del contador está claramente regulado. En otros, por ejemplo, en el mismo Pascal, el lenguaje estándar no define ni el valor final del contador ni las consecuencias de su cambio explícito en el bucle, pero no recomienda cambiar el contador explícitamente y usarlo al final. del bucle sin reinicializar. Un programa en Pascal que ignore esta recomendación puede producir resultados diferentes cuando se ejecute en diferentes sistemas y utilice diferentes traductores.
El problema se resuelve radicalmente en los lenguajes Ada y Kotlin : se considera que el contador se describe en el encabezado del bucle y simplemente no existe fuera de él. Incluso si el nombre del contador ya se usa en el programa, se usa una variable separada como contador dentro del bucle. Está prohibido asignar explícitamente cualquier valor al contador, solo puede cambiarse mediante el mecanismo interno del operador de bucle.
Como resultado, la construcción en Ada:
yo := 100 ; for i in ( 0. . 9 ) bucle ... bucle final del cuerpo del bucle ; k := yo ;Y en Kotlin:
valor i = 100 ; for ( i in 0. . 9 ){ ... cuerpo del bucle } val k = i ;aparentemente similar al ciclo de Pascal anterior, se interpreta sin ambigüedades: a la variable k se le asignará el valor 100, ya que la variable que utilicé fuera de este ciclo no tiene nada que ver con el contador i , que se crea y cambia dentro del ciclo . Tal aislamiento del contador es conveniente y seguro: no se requiere una descripción por separado y la probabilidad de errores aleatorios asociados con la destrucción accidental de variables externas al ciclo es mínima. Si un programador necesita incluir un ciclo con un contador en el código terminado, entonces no puede verificar si hay una variable con el nombre que ha elegido como contador, no agregar una descripción de un nuevo contador al encabezado del procedimiento correspondiente, no intente utilizar uno de los disponibles, pero en este momento de contadores "gratis". Simplemente escribe un bucle con un contador de variables, cuyo nombre le conviene, y puede estar seguro de que no se producirá ninguna colisión de nombres.
Un ciclo con un contador siempre se puede escribir como un ciclo condicional, antes del comienzo del cual se le asigna un valor inicial al contador, y la condición de salida es que el contador alcance el valor final; al mismo tiempo, se agrega al cuerpo del ciclo un operador para cambiar el contador en un paso dado. Sin embargo, los operadores especiales de un ciclo con contador se pueden traducir de manera más eficiente, ya que la forma formalizada de dicho ciclo permite el uso de instrucciones de procesador especiales para organizar ciclos.
Niklaus Wirth en un momento llamó al bucle con un contador "marginal", argumentando que tal construcción es redundante y debería excluirse de la sintaxis de los lenguajes de programación como no sistema. De acuerdo con este punto de vista, no había ningún ciclo con contador en el lenguaje de programación Oberon . Sin embargo, en el lenguaje Oberon-2 , creado por Wirth y Mössenböck en el desarrollo de Oberon, el bucle con contador FORapareció de nuevo en aras de la usabilidad práctica [1] .
En algunos lenguajes, como C y otros derivados de él, el bucle for, a pesar de la forma sintáctica de un bucle con un contador, es en realidad un bucle con una condición previa. Es decir, en C, la construcción de bucle:
para ( yo = 0 ; yo < 10 ; ++ yo ) { ... cuerpo del bucle }en realidad representa otra forma de notación de la construcción [2] :
yo = 0 _ mientras ( yo < 10 ) { ... cuerpo del bucle ++ i ; }Es decir, en la construcción for, primero se escribe una sentencia arbitraria de inicialización del ciclo, luego una condición de continuación y, finalmente, se realiza alguna operación después de cada cuerpo del ciclo (esto no tiene por qué ser un cambio en el contador). ; puede ser la edición de un puntero o alguna operación completamente extraña). Para lenguajes de este tipo, el problema descrito anteriormente se resuelve de manera muy simple: la variable contador se comporta de manera completamente predecible y, al final del ciclo, conserva su último valor.
Otra variante del ciclo es un ciclo que especifica la ejecución de alguna operación para objetos de un conjunto dado, sin especificar explícitamente el orden en que se enumeran estos objetos. Dichos ciclos se denominan conjuntos (así como ciclos de recolección , ciclos de vista ) y representan una declaración formal de la forma: "Realizar la operación X para todos los elementos incluidos en el conjunto M". El bucle conjunto, teóricamente, no determina de ninguna manera en qué orden se aplicará la operación a los elementos del conjunto, aunque los lenguajes de programación específicos, por supuesto, pueden especificar un orden específico para iterar sobre los elementos. La arbitrariedad permite optimizar la ejecución del ciclo organizando el acceso no en el orden del programador, sino en el orden más favorable. Con la posibilidad de ejecución paralela de varias operaciones, incluso es posible paralelizar la ejecución de un ciclo conjunto, cuando la misma operación se realiza simultáneamente en diferentes módulos de cómputo para diferentes objetos, mientras que el programa permanece lógicamente secuencial.
Los bucles conjuntos están disponibles en algunos lenguajes de programación ( C# , Eiffel , Java , JavaScript , Perl , Python , PHP , LISP , Tcl , etc.) y le permiten recorrer todos los elementos de una colección determinada de objetos . En la definición de dicho bucle, solo necesita especificar una colección de objetos y una variable, a la que en el cuerpo del bucle se le asignará el valor del objeto procesado actualmente (o una referencia a él). En diferentes lenguajes de programación, la sintaxis del operador es diferente:
C++ :
for ( type & item : set ) //compatible desde C++11 { //usar elemento }do# :
foreach ( escriba el elemento en el conjunto ) { //usando el elemento }Delfos :
for item in [ 1 .. 100 ] do begin //Using item (Este código fue probado en Delphi 2010) end ;Perl (orden estricto del primero al último):
foreach ( @set ) { #use $_ } # o for ( @set ) { #use $_ } # o foreach $item ( @set ) { #use $item }eiffel :
a través del conjunto como bucle de cursor : use cursor.item endJava :
for ( tipo de elemento : conjunto ) { //usando el elemento } for ( txtProperty en objObject ) { /* uso: objObject [txtProperty] */ }PHP :
foreach ( $arr as $item ) { /* usa $item*/ } //o foreach ( $arr as $key => $value ) { /* usa los valores de índice de $key y $value*/ } //o foreach ( $matriz como & $elemento ) { /*usar $elemento por referencia*/ }visual básico . neto :
Para cada elemento Como escriba En conjunto 'usar elemento Siguiente elemento foreach ($elemento en $conjunto) { # operaciones en $elemento }o
$conjunto | Para cada objeto { # operaciones con $_ } para elemento en iterator_instance : # usar elemento instancia_iterador . cada uno hace | artículo | # usar fin de elementoMuchos lenguajes de programación que tienen construcciones cíclicas en su sintaxis también tienen comandos específicos que te permiten violar el orden de operación de estas construcciones: el comando para salir temprano del ciclo y el comando para saltar la iteración.
El comando de salida anticipada se utiliza cuando es necesario abortar la ejecución de un bucle en el que aún no se ha alcanzado la condición de salida. Esto sucede, por ejemplo, cuando se detecta un error durante la ejecución del cuerpo del bucle, después de lo cual no tiene sentido seguir trabajando en el bucle.
Una instrucción de salida anticipada suele llamarse EXITo break, y su efecto es similar al de una instrucción de bifurcación incondicional ( goto) en la instrucción que sigue inmediatamente al bucle en el que se encuentra esta instrucción. Entonces, en el lenguaje C, los dos bucles siguientes funcionan exactamente igual:
// Aplicación de la sentencia break while ( < condition > ) { ... operadores si ( < error > ) se rompe ; ... operadores } ... continuación del programa // Fragmento similar sin interrupción while ( < condition > ) { ... operadores if ( < error > ) ir a romper_etiqueta ; ... operadores } romper_etiqueta : ... continuación del programaEn ambos casos, si se cumple la condición de <error> en el cuerpo del bucle, se realizará una transición a las declaraciones designadas como "continuación del programa". Por lo tanto, el operador loop early exit, de hecho, simplemente enmascara la rama incondicional, sin embargo, el uso de break es preferible a goto, ya que el comportamiento de break está claramente especificado por el lenguaje, potencialmente menos peligroso (por ejemplo, no hay posibilidad de equivocarse con la posición o el nombre de la etiqueta). Además, la salida anticipada explícita del ciclo no viola los principios de la programación estructurada.
Una sentencia ordinaria de salida anticipada termina el bucle en el que se encuentra directamente. En varios lenguajes de programación, la funcionalidad de este operador se amplía, le permite salir de varios bucles anidados (ver más abajo). En tales casos, el ciclo del que se va a salir se marca con una etiqueta, y la etiqueta se especifica en la instrucción de salida anticipada.
Este operador se usa cuando en la iteración del bucle actual es necesario omitir todos los comandos hasta el final del cuerpo del bucle. En este caso, el bucle en sí no debe interrumpirse, las condiciones de continuación o salida deben calcularse de la forma habitual.
En C y sus descendientes, el comando de salto de iteración es una declaración continueen una construcción de bucle. La acción de este operador es similar a un salto incondicional a la línea dentro del cuerpo del ciclo siguiendo su último comando. Por ejemplo, un código C que encuentre la suma de los elementos de una matriz y la suma de todos los elementos positivos de la matriz podría verse así:
int arr [ TAMAÑO DE ARRS ]; ... // Sumando por separado todos y solo los // elementos positivos de la matriz arr usando continue. int suma_todos = 0 ; int suma_pos = 0 ; for ( int i = 0 ; i < TAMAÑO ARRS ; ++ i ) { sum_all += arr [ i ]; if ( arr [ i ] <= 0 ) continuar ; sum_pos += arr [ i ]; } // Código similar c goto int sum_all = 0 ; int suma_pos = 0 ; for ( int i = 0 ; i < TAMAÑO ARRS ; ++ i ) { sum_all += arr [ i ]; if ( arr [ i ] <= 0 ) ir a cont_label ; sum_pos += arr [ i ]; cont_label : }El segundo fragmento muestra claramente cómo funciona continue: simplemente transfiere el control sobre el último comando del cuerpo del bucle, omitiendo la ejecución del comando de suma si el siguiente elemento de la matriz no cumple la condición. Por lo tanto, sum_pos acumula la suma de solo elementos positivos de la matriz.
Desde el punto de vista de la programación estructural, los comandos loop exit y iteration skip son redundantes, ya que su acción puede modelarse fácilmente por medios puramente estructurales. Además, según varios teóricos de la programación (en particular, Edsger Dijkstra), el hecho mismo de utilizar medios no estructurales en un programa, ya sea un salto incondicional clásico o cualquiera de sus formas especializadas, como romper o continuar, es evidencia de un algoritmo insuficientemente desarrollado para resolver el problema.
Sin embargo, en la práctica, el código del programa es a menudo un registro de un algoritmo formulado previamente ya existente, que no es conveniente volver a trabajar por razones puramente técnicas. Un intento de reemplazar el comando de salida temprana en dicho código con construcciones estructurales a menudo resulta ser ineficiente o engorroso. Por ejemplo, el fragmento de código anterior con el comando breakpodría escribirse así:
// Salida anticipada del bucle sin interrupción bool flag = false ; // indicador de finalización anticipada while ( < condición > && ! indicador ) { ... operadores si ( < error > ) { bandera = verdadero ; } más { ... operadores } } ... continuación del programaEs fácil asegurarse de que el fragmento funcionará de manera similar a los anteriores, la única diferencia es que, en lugar de salir directamente del bucle, la bandera de salida anticipada se establece en el lugar de verificación de un error, que se verifica más adelante en la condición regular para continuar el bucle. Sin embargo, para cancelar el comando de salida anticipada, se tuvo que agregar al programa una descripción de bandera y una segunda rama del operador condicional, y la lógica del programa se "difuminó" (la decisión de salir anticipadamente se toma en un solo lugar, y ejecutado en otro). Como resultado, el programa no se ha vuelto ni más simple, ni más corto, ni más claro.
La situación es algo diferente con el comando de iteración de salto. Por regla general, se reemplaza muy fácil y naturalmente por un operador condicional. Por ejemplo, el fragmento de suma de matriz anterior podría escribirse así:
int arr [ TAMAÑO DE ARRS ]; ... // Sumando por separado todos y solo los // elementos positivos de la matriz arr con reemplazo continue int sum_all = 0 ; int suma_pos = 0 ; for ( int i = 0 ; i < TAMAÑO ARRS ; ++ i ) { sum_all += arr [ i ]; if ( arr [ i ] > 0 ) // ¡Condición invertida! { sum_pos += arr [ i ]; } }Como puede ver, fue suficiente reemplazar la condición marcada con la opuesta y colocar la parte final del cuerpo del ciclo en una declaración condicional. Puede ver que el programa se ha vuelto más corto (debido a la eliminación del comando de iteración de salto) y al mismo tiempo más lógico (se ve directamente en el código que se resumen los elementos positivos).
Además, usar el comando de salto de iteración en un ciclo con una condición (bucle while) también puede provocar un error no obvio: si el cuerpo del ciclo, como sucede a menudo, termina con comandos para cambiar la(s) variable(s) del ciclo, entonces la iteración El comando skip también omitirá estos comandos, por lo que (dependiendo de la condición en la que se produzca el salto) puede producirse un bucle o una repetición de iteración que no se corresponde con el algoritmo. Entonces, si reemplazamos el bucle for con while en el ejemplo anterior, obtenemos lo siguiente:
int arr [ TAMAÑO DE ARRS ]; ... int suma_todos = 0 ; int suma_pos = 0 ; int i = 0 ; while ( i < ARRSIZE ) // El bucle se parece al anterior para ... { sum_all += arr [ i ]; if ( arr [ i ] <= 0 ) continuar ; sum_pos += arr [ i ]; ++ yo ; // ... pero este comando se omitirá al continuar // y el programa se repetirá }A pesar de su utilidad limitada y la capacidad de reemplazarlos con construcciones de otros lenguajes, los comandos de iteración de omisión y, especialmente, la salida anticipada del ciclo son extremadamente útiles en algunos casos, razón por la cual se conservan en los lenguajes de programación modernos.
Es posible organizar un bucle dentro del cuerpo de otro bucle. Este bucle se llamará bucle anidado . Un bucle anidado en relación con el bucle en cuyo cuerpo está anidado se denominará bucle interno y viceversa, un bucle en cuyo cuerpo hay un bucle anidado se denominará externo en relación con el anidado. Dentro del bucle anidado, a su vez, se puede anidar otro bucle, formando el siguiente nivel de anidamiento , y así sucesivamente. El número de niveles de anidamiento, por regla general, no está limitado.
El número total de ejecuciones del cuerpo del ciclo interno no excede el producto del número de iteraciones del ciclo interno y todos los externos. Por ejemplo, tomando tres bucles anidados uno dentro del otro, cada uno con 10 iteraciones, obtenemos 10 ejecuciones del cuerpo para el bucle exterior, 100 para el bucle de segundo nivel y 1000 en el bucle interior.
Uno de los problemas asociados con los bucles anidados es la organización de la salida anticipada de ellos. Muchos lenguajes de programación tienen un operador de terminación de bucle ( breaken C, exiten Turbo Pascal, lasten Perl , etc.), pero, por regla general, proporciona una salida solo del bucle del nivel desde el que se llamó. Llamarlo desde dentro de un ciclo anidado terminará solo ese ciclo interno, mientras que el ciclo externo continuará ejecutándose. El problema puede parecer exagerado, pero a veces surge cuando se programa un procesamiento de datos complejo, cuando el algoritmo requiere un aborto inmediato bajo ciertas condiciones, cuya presencia solo se puede verificar en un bucle profundamente anidado.
Hay varias soluciones al problema de salir de bucles anidados.
En la teoría de la programación, existe otra forma de construcción cíclica que es fundamentalmente diferente de las "clásicas", llamada ciclo de Dijkstra, en honor a Edsger Dijkstra , quien lo describió por primera vez. En la descripción clásica de Dijkstra, dicho ciclo se ve así:
hacer PAG 1 → S 1 , … PAG norte → S norte sobredosisAquí do , es el marcador del comienzo de la construcción del bucle, od es el marcador del final de la construcción del bucle, P i es la i - ésima condición de protección (una expresión lógica que puede ser verdadera o falsa), S i es la i -th comando vigilado . Un bucle consta de una o más ramas ( expresiones protegidas ), cada una de las cuales es un par de una condición de protección (o "guardias" para abreviar) y un comando protegido (está claro que en realidad el comando puede ser complejo).
Cuando se ejecuta el bucle de Dijkstra, las condiciones de protección se calculan en cada iteración. Si al menos una de ellas es verdadera, se ejecuta el comando protegido correspondiente, después de lo cual comienza una nueva iteración (si más de una condición de protección es verdadera, solo se ejecuta un comando protegido). Si todas las condiciones de protección son falsas, el ciclo termina. Es fácil ver que el ciclo de Dijkstra con una condición de guardia y un comando de guardia es, de hecho, un ciclo ordinario con una condición previa (el ciclo "while").
Aunque el bucle de Dijkstra se inventó en la década de 1970, no existen construcciones especiales para crearlo en los lenguajes de programación. La única excepción fue Oberon-07 , creado recientemente, el primer lenguaje de programación real que admitía explícitamente un bucle con múltiples ramas protegidas. Sin embargo, el ciclo de Dijkstra se puede modelar sin mucha dificultad utilizando las construcciones tradicionales de los lenguajes de programación estructurados. Aquí hay un ejemplo de su implementación en una de las formas posibles en el lenguaje Ada:
bucle si P1 entonces S1 ; ... elsif Pn luego Sn ; si no , salir ; terminar si ; bucle final ;Aquí P1-Pn son las condiciones de protección y S1-Sn son los comandos de protección correspondientes.
El bucle de Dijkstra es útil para implementar algunos cálculos repetitivos específicos que son inconvenientes para describir con construcciones de bucle más tradicionales. Por ejemplo, este ciclo representa naturalmente un autómata finito : cada rama corresponde a un estado del autómata, las condiciones guardadas se construyen de modo que en la iteración actual se seleccione la rama correspondiente al estado actual del autómata, y el código del guarda La instrucción asegura que los cálculos se realicen en el estado actual y la transición al siguiente (es decir, un cambio en las variables, después de lo cual la condición de protección de la rama deseada será verdadera en la siguiente iteración).
Es fácil ver que el ciclo de Dijkstra no contiene una condición explícita de continuar o salir, lo que no es considerado una bendición por todos los teóricos de la programación. Por lo tanto, se propuso una construcción complicada del ciclo de Dijkstra, llamada "ciclo de la araña". En la misma notación, se ve así:
hacer PAG 1 → S 1 , … P norte → S norte afuera Q 1 → T 1 , … Q norte → T norte más mi sobredosisAquí, después del marcador , outse agregan ramas de finalización , que consisten en condiciones de salida Q i y comandos de finalización T i . Además, se ha agregado una rama de finalización alternativa elsecon el comando E.
El bucle de araña se ejecuta así:
La estructura del ciclo 'araña' permite una descripción extremadamente estricta de las condiciones para la ejecución del ciclo. De acuerdo con las posiciones teóricas, la rama de finalización alternativa no debe usarse como una de las opciones para terminar correctamente el bucle (todas esas opciones deben formatearse como ramas de finalización correspondientes con una condición explícita), solo sirve para rastrear la situación cuando, por alguna razón, por alguna razón, el ciclo comenzó a funcionar de manera anormal. Es decir, el comando alt solo puede analizar las causas del error y presentar los resultados del análisis.
Aunque el soporte explícito a nivel de sintaxis para este ciclo no existe en ningún lenguaje de programación, el ciclo de araña, como el ciclo de Dijkstra, se puede modelar utilizando construcciones estructurales tradicionales.