Articulo de referencia

Flujo de control

En software , el flujo de control describe cómo progresa la ejecución de un comando al siguiente. En muchos contextos, como el código máquina y un lenguaje de programación imper...

En software , el flujo de control describe cómo progresa la ejecución de un comando al siguiente. En muchos contextos, como el código máquina y un lenguaje de programación imperativo , el control progresa secuencialmente (al comando ubicado inmediatamente después del comando que se está ejecutando actualmente), excepto cuando un comando transfiere el control a otro punto ; en ese caso, el comando se clasifica como un comando de flujo de control. Dependiendo del contexto, se utilizan otros términos en lugar de comando . Por ejemplo, en código máquina, el término típico es instrucción y en un lenguaje imperativo, el término típico es sentencia .

Si bien un lenguaje imperativo codifica explícitamente el flujo de control, los lenguajes de otros paradigmas de programación se centran menos en este aspecto. Un lenguaje declarativo especifica los resultados deseados sin prescribir un orden de operaciones. Un lenguaje funcional utiliza tanto construcciones del lenguaje como funciones para controlar el flujo, aunque generalmente no se las denomine sentencias de control de flujo.

En un conjunto de instrucciones de la unidad central de procesamiento , una instrucción de control de flujo suele modificar el contador de programa y puede ser una bifurcación incondicional (también conocida como salto) o una bifurcación condicional. Un enfoque alternativo es la predicación , que habilita instrucciones de forma condicional en lugar de realizar bifurcaciones.

Una transferencia de flujo de control asíncrona , como una interrupción o una señal, altera el flujo normal de control hacia un manejador antes de devolver el control al punto donde se interrumpió.

Una forma de atacar el software es redirigir el flujo de ejecución. Se utilizan diversas técnicas de integridad del flujo de control , como los canarios de pila , la protección contra desbordamiento de búfer , las pilas en la sombra y la verificación de punteros de tabla virtual , para defenderse de estos ataques. [ 1 ] [ 2 ] [ 3 ]

Estructura

El flujo de control está estrechamente relacionado con la estructura del código. El control fluye según las líneas definidas por la estructura y las reglas de ejecución del lenguaje. Este concepto general de estructura no debe confundirse con la programación estructurada , que limita la estructura a la secuenciación, la selección y la iteración basadas en la organización en bloques .

Secuencia

La ejecución secuencial es la estructura más básica. Si bien no todo el código es secuencial por naturaleza, el código imperativo sí lo es.

Etiqueta

Una etiqueta identifica una posición en el código fuente . Algunas instrucciones de control de flujo hacen referencia a una etiqueta para que el control salte a la línea etiquetada. Aparte de marcar una posición, una etiqueta no tiene ningún otro efecto.

Algunos lenguajes limitan una etiqueta a un número que a veces se denomina número de línea , aunque esto implica el índice inherente de la línea, no una etiqueta. No obstante, estas etiquetas numéricas suelen requerir un incremento de arriba a abajo en un archivo, incluso si no son secuenciales. Por ejemplo, en BASIC:

10 SEA X = 3 20 IMPRIMIR X 30 IR A 10

En muchos lenguajes de programación, una etiqueta es un identificador alfanumérico que suele aparecer al principio de una línea, seguido inmediatamente por dos puntos. Por ejemplo, el siguiente código C define una etiqueta Successen la línea 3 que identifica un punto de destino de salto en la primera instrucción que la sigue (línea 4).

void f ( bool ok ) {si ( ok ) {ir al éxito ;}devolver ;éxito :printf ( "OK" );}

Bloquear

La mayoría de los lenguajes permiten organizar secuencias de código en bloques . Al usarlos con una instrucción de control, el inicio de un bloque indica el punto de salto. Por ejemplo, en el siguiente código C (que utiliza llaves para delimitar un bloque), el control salta de la línea 1 a la 4 si `done` es falso.

si ( hecho ) {printf ( "Todo listo" );} demás {printf ( "Sigo trabajando en ello" );}

Control

Se han ideado numerosos comandos de control para los lenguajes de programación. Esta sección describe las construcciones más destacadas y está organizada por funcionalidad.

Función

Una función proporciona control de flujo, de modo que cuando se la llama, la ejecución salta al inicio del código de la función y, cuando esta finaliza, el control regresa al punto de llamada. En el siguiente código C, el control salta de la línea 6 a la 2 para llamar a la función foo(). Luego, tras completar el cuerpo de la función (imprimiendo "Hola"), el control regresa al punto de llamada, línea 7.

void foo () {printf ( "Hola" );}barra vacía () {foo ();printf ( "Hecho" );}

Rama

Un comando de bifurcación mueve el punto de ejecución desde el punto del código que contiene el comando hasta el punto que este especifica.

Saltar

Un comando de salto ramifica incondicionalmente el control a otro punto del código, y es la forma más básica de controlar el flujo del código.

En un lenguaje de alto nivel, esto se suele proporcionar como una instrucción goto . Aunque la palabra clave puede estar en mayúsculas o minúsculas, o ser una o dos palabras dependiendo del lenguaje, es como: . Cuando el control llega a una instrucción goto, salta a la instrucción que sigue a la etiqueta indicada. Muchos informáticos, incluido Dijkstra , consideran que la instrucción goto es perjudicial .goto label

Rama condicional

Una instrucción condicional transfiere el control en función del valor de una expresión booleana . Algunas variaciones comunes incluyen:

si-ir a
Salta a una etiqueta en función de una condición; una instrucción de programación de alto nivel que imita fielmente una instrucción de código máquina similar.
Si-entonces
En lugar de limitarse a un salto, se ejecuta una instrucción o bloque si la expresión es verdadera. En un lenguaje que no incluye la thenpalabra clave `if`, esto se puede denominar una instrucción `if`.
Si-entonces-si no
Similar a una estructura condicional if-then, pero con una segunda acción que se realiza si la condición es falsa. En un lenguaje que no incluye la thenpalabra clave `if-else`, esto se denomina sentencia if-else.
Anidado
Las sentencias condicionales suelen estar anidadas dentro de otras sentencias condicionales.
Aritmética si
Las primeras versiones de Fortran incluían una instrucción condicional aritmética (también conocida como condicional de tres vías) que comprobaba si un valor numérico era negativo, cero o positivo. Esta instrucción se consideró obsoleta en Fortran-90 y se eliminó a partir de Fortran 2018.
Operador
Algunos lenguajes tienen una forma de operador , como el operador condicional ternario .
Cuando y a menos que
Perl complementa un estilo C ifcon wheny unless.
Mensajes
Smalltalk utiliza ifTruemensajes ifFalsepara implementar condicionales, en lugar de una construcción del lenguaje.

El siguiente código Pascal muestra una estructura if-then-else simple. La sintaxis es similar en Ada :

si a > 0 entonces escribir ( " " ) de lo contrario escribir ( " no " ) ;

Cª :

if ( a > 0 ) { puts ( "sí" ); } else { puts ( "no" ); }

En bash :

if [ $a -gt 0 ] ; then echo "sí" else echo "no" fi

En Python :

if a > 0 : print ( "sí" ) else : print ( "no" )

En Lisp :

( principio ( si ( másp a ) "sí" "no" ))

Ramal multidireccional

Una bifurcación múltiple salta el control según los valores coincidentes. Normalmente hay una disposición para una acción predeterminada si no se encuentra ninguna coincidencia. Una instrucción switch puede permitir optimizaciones del compilador, como tablas de búsqueda . En lenguajes dinámicos , los casos pueden no limitarse a expresiones constantes y podrían extenderse a la coincidencia de patrones , como en el ejemplo de script de shell de la derecha, donde *)se implementa el caso predeterminado como un glob que coincide con cualquier cadena. La lógica de casos también puede implementarse en forma funcional, como en la instrucción SQLdecode .

El siguiente código Pascalcase muestra una instrucción switch relativamente simple. Pascal utiliza la palabra clave en lugar de switch.

caso algúnChar de 'a' : acciónEnA ; 'x' : acciónEnX ; 'y' , 'z' : acciónEnYyandZ ; de lo contrario acciónEnNoCoincidir ; fin ;

En Ada :

caso someChar es cuando ' a ' => acciónEnA ; cuando ' x ' => acciónEnX ; cuando ' y ' | ' z ' => acciónEnYyZ ; cuando otros => acciónEnNoCoincidir ; fin ;

Cª :

switch ( someChar ) { case 'a' : actionOnA ; break ; case 'x' : actionOnX ; break ; case 'y' : case 'z' : actionOnYandZ ; break ; default : actionOnNoMatch ; }

En Bash :

caso $someChar en a ) acciónEnA ;; x ) acciónEnX ;; [ yz ]) acciónEnYandZ ;; * ) acciónEnNoCoincidir ;; esac

En Lisp :

( case some-char (( #\a ) acción-sobre-a ) (( #\x ) acción-sobre-x ) (( #\y #\z ) acción-sobre-y-y-y-z ) ( else acción-sobre-sin-coincidencia ))

En Fortran :

seleccionar caso ( algúnChar ) caso ( 'a' ) acciónEnA caso ( 'x' ) acciónEnX caso ( 'y' , 'z' ) acciónEnYyZ caso predeterminado acciónEnNoCoincidir fin seleccionar

Bucle

Tipos básicos de bucles de programa

Un bucle es una secuencia de instrucciones, el cuerpo del bucle, que se ejecuta varias veces según el estado de ejecución. El cuerpo se ejecuta una vez por cada elemento de una colección ( iteración definida ), hasta que se cumple una condición ( iteración indefinida ) o de forma infinita . Un bucle dentro del cuerpo del bucle se denomina bucle anidado . [ 4 ] [ 5 ] [ 6 ] Se puede salir de un bucle antes de tiempo mediante una instrucción break. [ 7 ] [ 8 ]

En un lenguaje de programación funcional , como Haskell y Scheme , tanto los procesos recursivos como los iterativos se expresan con procedimientos recursivos de cola en lugar de construcciones de bucle sintácticas.

Numérico

Un bucle relativamente sencillo pero útil itera sobre un rango de valores numéricos. Una forma simple comienza en un valor entero, termina en un valor entero mayor e itera para cada valor entero intermedio. A menudo, el incremento puede ser cualquier valor entero (incluso negativo, para iterar desde un valor mayor a uno menor).

Ejemplo en BASIC:

PARA I = 1 HASTA N xxx SIGUIENTE I

Ejemplo en Pascal:

para I := 1 a N hacer begin xxx end ;

Ejemplo en Fortran:

HACER I = 1 , N xxx FIN HACER

En muchos lenguajes de programación, solo se pueden usar números enteros de forma fiable. Dado que los números de coma flotante se representan de forma imprecisa debido a las limitaciones del hardware, el siguiente bucle podría iterar 9 o 10 veces, dependiendo de diversos factores como el error de redondeo, el hardware y el compilador. Además, si el incremento de X se produce mediante sumas repetidas, los errores de redondeo acumulados pueden provocar que el valor de X en cada iteración difiera significativamente de la secuencia habitual esperada de 0,1, 0,2, 0,3, ..., 1,0.

para X := 0.1 paso 0.1 a 1.0 hacer

Controlado por condiciones

Algunos bucles se ejecutan hasta que se cumple una condición. Algunas variantes comprueban la condición al inicio del bucle; otras, al final. Si la comprobación se realiza al inicio, el cuerpo del bucle puede omitirse por completo. Si se realiza al final, el cuerpo siempre se ejecuta al menos una vez.

Ejemplo en Visual Basic:

HACER MIENTRAS ( prueba ) xxx BUCLE

Ejemplo en Pascal:

repetir xxx hasta la prueba ;

Ejemplo en la familia C de pre-prueba:

mientras ( prueba ) { xxx }

Ejemplo de prueba posterior en la familia C:

hacer xxx mientras ( prueba );

Aunque se utiliza la forpalabra clave, el bucle de tres partes al estilo C es una construcción basada en condiciones, no en números. La segunda parte, la condición, se evalúa antes de cada iteración, por lo que el bucle es una prueba previa. La primera parte sirve para inicializar el estado, y la tercera para incrementarlo para la siguiente iteración, pero ambos aspectos pueden realizarse en otro lugar. El siguiente código C implementa la lógica de un bucle numérico que itera para i desde 0 hasta n-1.

para ( int i = 0 ; i < n ; ++ i ) { xxx }

Enumeración

Algunas estructuras de bucle enumeran los elementos de una colección, iterando para cada elemento.

Ejemplo en Smalltalk:

someCollection hacer: [ : eachElement | xxx ] .

Ejemplo en Pascal:

para cada elemento en la colección hacer begin xxx end ;

Ejemplo en Raku:

para cada ( elemento ; miColección ) { xxx } 

Ejemplo en TCL:

para cada someArray { xxx }

Ejemplo en PHP:

para cada ( $someArray como $k => $v ) { xxx }

Ejemplo en Java:

Colección < String > coll ; para ( String s : coll ) {}

Ejemplo en C#:

para cada ( cadena s en myStringCollection ) { xxx }

Ejemplo en PowerShell donde 'foreach' es un alias de 'ForEach-Object':

algunaColección | para cada { $_ }

Ejemplo en Fortran:

para todos ( índice = primero : último : paso ... )

Scala cuenta con expresiones `for` , que generalizan los bucles controlados por colecciones y también admiten otros usos, como la programación asíncrona . Haskell dispone de expresiones `do` y comprensiones, que en conjunto ofrecen una funcionalidad similar a la de las expresiones `for` en Scala.

Infinito

En programación informática , un bucle infinito (o bucle sin fin) [ 9 ] [ 10 ] es una secuencia de instrucciones que, tal como está escrita, continuará indefinidamente, a menos que ocurra una intervención externa, como apagar la alimentación mediante un interruptor o desconectar un enchufe. Puede ser intencional.

No existe un algoritmo general para determinar si un programa informático contiene un bucle infinito o no; este es el problema de la parada .

Problema de bucle y medio

Las estructuras de bucle comunes a veces dan como resultado código duplicado, ya sean sentencias repetidas o condiciones repetidas. Esto surge por varias razones y existen varias soluciones propuestas para eliminar o minimizar la duplicación de código. [ 11 ] Además de la solución no estructurada tradicional de una sentencia goto , [ 12 ] las soluciones estructuradas generales incluyen tener una condición ( sentencia if ) dentro del bucle (posiblemente duplicando la condición pero no las sentencias) o encapsular la lógica repetida en una función (de modo que hay una llamada a la función duplicada, pero las sentencias no se duplican). [ 11 ]

Un caso común es cuando el inicio del bucle siempre se ejecuta, pero el final puede omitirse en la última iteración. [ 12 ] Esto fue denominado por Dijkstra como un bucle que se realiza " n y media veces", [ 13 ] y ahora se llama el problema del bucle y medio . [ 8 ] Los casos comunes incluyen leer datos en la primera parte, verificar el final de los datos y luego procesar los datos en la segunda parte; o procesar, verificar el final y luego prepararse para la siguiente iteración. [ 12 ] [ 8 ] En estos casos, la primera parte del bucle se ejecuta .norte{\displaystyle n} veces, pero la segunda parte solo se ejecutanorte1{\displaystyle n-1}veces .

Este problema fue reconocido al menos desde 1967 por Knuth, y Wirth sugirió resolverlo mediante la salida temprana del bucle. [ 14 ] Desde la década de 1990, esta ha sido la solución más comúnmente enseñada, utilizando una instrucción break , como en: [ 8 ]

sentencias de bucle sentencias if condición sentencias break repetir

Una sutileza de esta solución es que la condición es la opuesta a una condición while habitual : reescribir while condición ... repetir con una salida en el medio requiere invertir la condición: bucle ... si no condición salir ... repetir . La estructura de control bucle con prueba en el medio admite explícitamente el caso de uso de bucle y medio, sin invertir la condición. [ 14 ]

No estructurado

Una estructura de bucle proporciona criterios de finalización estructurados que dan como resultado otra iteración o la continuación de la ejecución después de la instrucción del bucle. Sin embargo, muchos lenguajes admiten diversas estructuras de control de flujo no estructuradas.

Próxima iteración temprana
Algunos lenguajes proporcionan una construcción que salta el control al principio del cuerpo del bucle para la siguiente iteración; por ejemplo, continue(el más común), skip, [ 15 ]cycle (Fortran), o next(Perl y Ruby).
Rehacer iteración
Algunos lenguajes, como Perl [ 16 ] y Ruby, [ 17 ] tienen una redoinstrucción que salta al inicio del cuerpo para la misma iteración.
Reanudar
Ruby tiene una retryinstrucción que reinicia todo el bucle desde la primera iteración. [ 18 ]
Salida anticipada

La salida temprana salta el control a después del cuerpo del bucle [ 19 ] [ 8 ] Por ejemplo, al buscar en una lista, puede detener el bucle cuando se encuentra el elemento. Algunos lenguajes de programación proporcionan una instrucción como break(la mayoría de los lenguajes), Exit(Visual Basic) o last(Perl).

En el siguiente código Ada, el bucle finaliza cuando X es 0.

bucle Obtener ( X ); si X = 0 entonces salir ; fin si ; HacerAlgo ( X ); fin bucle ;

Un estilo más idiomático utiliza exit when:

bucle Obtener ( X ); salir cuando X = 0 ; HacerAlgo ( X ); fin del bucle ;

Python admite la ejecución condicional de código dependiendo de si un bucle se ha cerrado prematuramente (con una breakinstrucción `if`) o no, mediante el uso de una cláusula `else` dentro del bucle. En el siguiente código Python, la elsecláusula `else` está vinculada a la forinstrucción `if`, y no a la ifinstrucción interna. Tanto `if` forcomo while`else` de Python admiten dicha cláusula `else`, que se ejecuta solo si no se ha cerrado prematuramente el bucle.

for n in set_of_numbers : if isprime ( n ): print ( "El conjunto contiene un número primo" ) break else : print ( "El conjunto no contenía ningún número primo" )
Descansos de varios niveles

Algunos lenguajes admiten la salida de bucles anidados; en teoría, se les llama saltos multinivel . Un ejemplo de uso común es la búsqueda en una tabla multidimensional. Esto se puede hacer mediante saltos multinivel (salir de N niveles), como en bash [ 20 ] y PHP, [ 21 ] o mediante saltos etiquetados (salir y continuar en una etiqueta dada), como en Ada, Go, Java, Rust y Perl. [ 22 ] Las alternativas a los saltos multinivel incluyen saltos simples, junto con una variable de estado que se prueba para salir de otro nivel; excepciones, que se capturan en el nivel al que se sale; colocar los bucles anidados en una función y usar return para efectuar la terminación de todo el bucle anidado; o usar una etiqueta y una instrucción goto. Ni C ni C++ tienen actualmente saltos multinivel ni bucles con nombre, y la alternativa habitual es usar goto para implementar un salto etiquetado. [ 23 ] Sin embargo, se propuso la inclusión de esta característica, [ 24 ] y se agregó a C2Y, [ 25 ] siguiendo la sintaxis de Java. Python no tiene un salto de línea o continuación multinivel; esto se propuso en PEP 3136 y se rechazó con base en que la complejidad adicional no justificaba el uso legítimo poco frecuente. [ 26 ]

La noción de rupturas multinivel es de cierto interés en la informática teórica , porque da lugar a lo que hoy se conoce como la jerarquía de Kosaraju . [ 27 ] En 1973, S. Rao Kosaraju perfeccionó el teorema del programa estructurado al demostrar que es posible evitar la adición de variables adicionales en la programación estructurada, siempre que se permitan rupturas multinivel de profundidad arbitraria en los bucles. [ 28 ] Además, Kosaraju demostró que existe una jerarquía estricta de programas: para cada entero n , existe un programa que contiene una ruptura multinivel de profundidad n que no puede reescribirse como un programa con rupturas multinivel de profundidad menor que n sin introducir variables adicionales. [ 27 ]

En su libro de texto de 2004, David Watt utiliza la noción de secuenciador de Tennent para explicar la similitud entre las interrupciones multinivel y las sentencias de retorno. Watt señala que una clase de secuenciadores conocidos como secuenciadores de escape , definidos como "secuenciadores que finalizan la ejecución de un comando o procedimiento que los contiene textualmente", abarcan tanto las interrupciones de bucles (incluidas las interrupciones multinivel) como las sentencias de retorno. Sin embargo, en su implementación habitual, los secuenciadores de retorno también pueden contener un valor (de retorno), mientras que el secuenciador de interrupción, tal como se implementa en los lenguajes contemporáneos, generalmente no puede. [ 29 ]

Prueba intermedia

La siguiente estructura fue propuesta por Dahl en 1972: [ 30 ]

bucle bucle xxx1 leer(carácter); mientras prueba; mientras no esté al final del archivo; xxx2 escribir(carácter); repetir ; repetir ;

La construcción aquí puede pensarse como un bucle do con la comprobación while en el medio, lo que permite una lógica clara de bucle y medio . Además, al omitir componentes individuales, esta única construcción puede reemplazar varias construcciones en la mayoría de los lenguajes de programación. Si se omite xxx1 , obtenemos un bucle con la prueba en la parte superior (un bucle while tradicional ). Si se omite xxx2 , obtenemos un bucle con la prueba en la parte inferior, equivalente a un bucle do while en muchos lenguajes. Si se omite while , obtenemos un bucle infinito. Esta construcción también permite mantener la misma polaridad de la condición incluso cuando está en el medio, a diferencia de la salida temprana, que requiere invertir la polaridad (agregar un not ), [ 14 ] funcionando como until en lugar de while .

Esta estructura no cuenta con un amplio soporte, ya que la mayoría de los lenguajes utilizan en su lugar if ... break para la salida anticipada condicional.

Esto es compatible con algunos lenguajes, como Forth , donde la sintaxis es BEGIN ... WHILE ... REPEAT, [ 31 ] y los lenguajes de script de shell Bourne shell ( sh) y bash , donde la sintaxis es while ... do ... done o until ... do ... done , como: [ 32 ] [ 33 ]

mientras la instrucción-1 instrucción-2 ... condición hacer instrucción-a instrucción-b ... hecho

La sintaxis del shell funciona porque el bucle while (o until ) acepta una lista de comandos como condición, [ 34 ] formalmente:

mientras se ejecutan los comandos de prueba ; hacer los comandos consecuentes ; hecho

El valor (estado de salida) de la lista de comandos de prueba es el valor del último comando, y estos se pueden separar con saltos de línea, lo que da como resultado la forma idiomática anterior.

Es posible construir estructuras similares en C y C++ con el operador coma , y ​​en otros lenguajes con construcciones similares , que permiten encajar una lista de instrucciones en la condición while:

mientras ( declaración_1 , declaración_2 , condición ) { declaración_a ; declaración_b ; }

Aunque legal, esto es marginal y se utiliza principalmente, si acaso, solo para casos cortos de modificación y posterior prueba, como en: [ 35 ]

mientras ( read_string ( s ), strlen ( s ) > 0 ) { // ... }

Variantes e invariantes de bucles

Las variantes e invariantes de bucle se utilizan para expresar la corrección de los bucles. [ 36 ]

En términos prácticos, una variante de bucle es una expresión entera con un valor inicial no negativo. El valor de la variante debe disminuir durante cada iteración del bucle, pero nunca debe volverse negativo durante su correcta ejecución. Las variantes de bucle se utilizan para garantizar que los bucles finalicen.

Un invariante de bucle es una aserción que debe ser verdadera antes de la primera iteración y permanecer verdadera después de cada iteración. Esto implica que, cuando un bucle finaliza correctamente, tanto la condición de salida como el invariante de bucle se cumplen. Los invariantes de bucle se utilizan para supervisar propiedades específicas de un bucle durante iteraciones sucesivas.

Algunos lenguajes de programación, como Eiffel, incluyen soporte nativo para variantes e invariantes de bucles. En otros casos, el soporte es un complemento, como la especificación del lenguaje de modelado de Java para las sentencias de bucle en Java .

Sublenguaje de bucle

Algunos dialectos de Lisp proporcionan un sublenguaje extenso para describir bucles. Un ejemplo temprano se puede encontrar en Conversional Lisp of Interlisp . Common Lisp [ 37 ] proporciona una macro Loop que implementa dicho sublenguaje.

Tabla de referencias cruzadas del sistema de bucles

  1. a no se considera un bucle infinito para este propósito, porque no es una estructura de lenguaje dedicada.while (true)
  2. a b c d e f g h El bucle de Ces una construcción de bucle general, no específicamente una de conteo, aunque a menudo se utiliza para eso.for (init; test; increment)
  3. a b c Se pueden realizar saltos profundos en APL, C, C++ y C# mediante el uso de etiquetas y goto.
  4. En PHP 5añadió la iteración sobre objetos
  5. a b c d e f Un bucle de conteo se puede simular iterando sobre una lista incremental o un generador, por ejemplo, el de Python.range()
  6. a b c d e Se pueden lograr rupturas profundas mediante el uso del manejo de excepciones.
  7. a No hay ninguna construcción especial, ya que lawhilefunción se puede utilizar para esto.
  8. a No existe una estructura especial, pero los usuarios pueden definir funciones de bucle generales.
  9. a ElC++11introdujo elrango basado en. En laSTL, hay unastd::for_eachplantillaque puede iterar sobrecontenedoresy llamar a unafunción unariapara cada elemento. [ 38 ] La funcionalidad también se puede construir comouna macroen estos contenedores. [ 39 ]
  10. El bucle numérico se realiza mediante la iteración a través de un intervalo entero; la salida anticipada se logra incluyendo una condición adicional para la salida.
  11. Eiffel admite una palabra reservadaretry, sin embargo, se utiliza enel manejo de excepciones, no en el control de bucles.
  12. a RequiereJava Modeling Language(JML).
  13. a Requiere que las variantes de bucle sean números enteros; no se admiten variantes transfinitas.Eiffel: Por qué las variantes de bucle son números enteros
  14. D admite colecciones infinitas y la capacidad de iterar sobre ellas. Esto no requiere ninguna construcción especial.
  15. Se pueden lograr rupturas profundas utilizando procedimientosGO TO .
  16. Common Lisp es anterior al concepto de tipo de colección genérico.
  17. a b El bucle general de Odinadmite atajos de sintaxis para bucles condicionales y bucles infinitos.for

No local

Muchos lenguajes de programación, especialmente aquellos que favorecen estilos de programación más dinámicos, ofrecen construcciones para el control de flujo no local que permiten que la ejecución salte del punto de ejecución actual a un punto predefinido . A continuación se presentan algunos ejemplos destacados.

Manejo de condiciones

Los primeros compiladores de Fortran admitían instrucciones para manejar condiciones excepcionales, incluyendo IF ACCUMULATOR OVERFLOW, IF QUOTIENT OVERFLOW, y IF DIVIDE CHECK. En aras de la independencia de la máquina, no se incluyeron en FORTRAN IV ni en el estándar Fortran 66. Sin embargo, desde Fortran 2003 es posible comprobar si existen problemas numéricos mediante llamadas a funciones del IEEE_EXCEPTIONSmódulo.

PL/I tiene unas 22 condiciones estándar (por ejemplo, ZERODIVIDE SUBSCRIPTRANGE ENDFILE) que se pueden generar y que pueden ser interceptadas por: ON condición acción; Los programadores también pueden definir y usar sus propias condiciones con nombre.

Al igual que con la instrucción if no estructurada , solo se puede especificar una instrucción, por lo que en muchos casos se necesita un GOTO para decidir dónde debe reanudarse el flujo de control.

Desafortunadamente, algunas implementaciones tenían una sobrecarga sustancial tanto en espacio como en tiempo (especialmente SUBSCRIPTRANGE), por lo que muchos programadores trataron de evitar el uso de condiciones.

Un ejemplo típico de sintaxis:

EN CONDICIÓN IR A la etiqueta

Manejo de excepciones

Muchos lenguajes modernos admiten de forma nativa el manejo de excepciones . Generalmente, el flujo de control de excepciones comienza con la generación de un objeto de excepción. El control se dirige entonces al manejador de excepciones más interno de la pila de llamadas . Si el manejador gestiona la excepción, el flujo de control vuelve a la normalidad. De lo contrario, el control se extiende a los manejadores contenedores hasta que uno de ellos gestiona la excepción o el programa alcanza el ámbito más externo y finaliza. A medida que el control fluye hacia manejadores progresivamente externos, aspectos que normalmente ocurrirían, como la eliminación de elementos de la pila de llamadas, se gestionan automáticamente.

El siguiente código C++ demuestra el manejo estructurado de excepciones. Si una excepción se propaga desde la ejecución de doSomething()y el tipo de objeto de excepción coincide con uno de los tipos especificados en una cláusula catch, entonces esa cláusula se ejecuta. Por ejemplo, si una excepción de tipo SomeExceptionse propaga mediante doSomething(), entonces el control salta de la línea 2 a la 4 y se imprime el mensaje "Caught SomeException" y luego el control salta a después de la tryinstrucción, línea 8. Si se propaga una excepción de cualquier otro tipo, entonces el control salta de la línea 2 a la 6. Si no hay excepción, entonces el control salta de 2 a 8.

intentar {hacerAlgo ();} catch ( const SomeException & e )std :: println ( " Se ha producido una excepción: {}" , e.what ());} atrapar (...) {std :: println ( "Error desconocido" );}hacerSiguienteCosa ();

Muchos lenguajes utilizan las palabras clave de C++ ( throw, tryy catch), pero algunos lenguajes utilizan otras palabras clave. Por ejemplo, Ada utiliza exceptionpara introducir un manejador de excepciones y whenen lugar de catch. AppleScript incorpora marcadores de posición en la sintaxis para extraer información sobre la excepción, como se muestra en el siguiente código AppleScript.

intentar establecer myNumber a myNumber / 0 en caso de error e número n de f a t resultado parcial pr si ( e = "No se puede dividir por cero" ) entonces mostrar diálogo "No debe hacer eso" fin intentar

En muchos lenguajes (incluidos Object Pascal, D, Java, C# y Python), una finallycláusula al final de una tryinstrucción se ejecuta al final de la instrucción try, independientemente de si se propaga una excepción desde el resto del bucle tryo no. El siguiente código C# garantiza que el flujo streamse cierre.

FileStream stream = null ; try { stream = new FileStream ( "logfile.txt" , FileMode.Create ); return ProcessStuff ( stream ) ; } finally { if ( stream ! = null ) { stream.Close ( ) ; } }

Dado que este patrón es común, C# proporciona la usinginstrucción para garantizar la limpieza. En el siguiente código, incluso si ProcessStuff() propaga una excepción, el streamobjeto se libera. La instrucción de Python withy el argumento de bloque de Ruby File.opense utilizan con un efecto similar.

using ( FileStream stream = new ( "logfile.txt" , FileMode . Create )) { return ProcessStuff ( stream ); }

Continuación

En informática , una continuación es una representación abstracta del estado de control de un programa . Una continuación implementa ( reifica ) el estado de control del programa; es decir, es una estructura de datos que representa el proceso computacional en un punto determinado de su ejecución. El lenguaje de programación puede acceder a esta estructura de datos, en lugar de que permanezca oculta en el entorno de ejecución . Las continuaciones son útiles para codificar otros mecanismos de control en lenguajes de programación, como excepciones , generadores , corrutinas , etc.

La "continuación actual" o "continuación del paso de cálculo" es aquella que, desde la perspectiva de la ejecución del código, se derivaría del punto actual de la ejecución del programa. El término " continuaciones" también puede referirse a las continuaciones de primera clase, que son construcciones que permiten a un lenguaje de programación guardar el estado de ejecución en cualquier punto y volver a ese punto posteriormente en el programa, posiblemente varias veces.

Generador

En informática , un generador es una rutina que se puede usar para controlar el comportamiento iterativo de un bucle . Todos los generadores son también iteradores . [ 40 ] Un generador es muy similar a una función que devuelve un array , ya que un generador tiene parámetros , se puede llamar y genera una secuencia de valores. Sin embargo, en lugar de construir un array que contenga todos los valores y devolverlos todos a la vez, un generador produce los valores uno a uno, lo que requiere menos memoria y permite que quien lo llama comience a procesar los primeros valores de inmediato. En resumen, un generador se parece a una función pero se comporta como un iterador .

Los generadores pueden implementarse en términos de construcciones de flujo de control más expresivas, como corrutinas o continuaciones de primera clase . [ 41 ] Los generadores, también conocidos como semicorrutinas, [ 42 ] son ​​un caso especial de (y más débiles que) las corrutinas, ya que siempre devuelven el control al llamador (al pasar un valor de vuelta), en lugar de especificar una corrutina a la que saltar; véase la comparación de corrutinas con generadores .

Corutina

Las corrutinas son componentes de programas informáticos que pueden suspenderse y reanudarse —generalizando las subrutinas— para la multitarea cooperativa . Son muy adecuadas para implementar componentes de programas comunes como tareas cooperativas , excepciones , bucles de eventos , iteradores , listas infinitas y tuberías .

Se las ha descrito como "funciones cuya ejecución se puede pausar". [ 43 ]

Melvin Conway acuñó el término corrutina en 1958 cuando lo aplicó a la construcción de un programa de ensamblaje . [ 44 ] La primera explicación publicada de la corrutina apareció más tarde, en 1963. [ 45 ]

VEN DE

En programación informática , COMEFROM es una instrucción de control de flujo que hace que el flujo de control salte a la instrucción siguiente cuando el control alcanza el punto especificado por el argumento COMEFROM. Esta instrucción pretende ser lo opuesto a goto y se considera más una broma que una norma seria de informática . A menudo, el punto de salto especificado se identifica con una etiqueta . Por ejemplo, COMEFROM xespecifica que cuando el control alcanza la etiqueta x, el control continúa en la instrucción siguiente a COMEFROM.

Una diferencia importante con goto es que goto depende de la estructura local del código, mientras que COMEFROM depende de la estructura global. Una instrucción goto transfiere el control cuando este llega a dicha instrucción, pero COMEFROM requiere que el procesador (es decir, el intérprete) busque instrucciones COMEFROM para que, cuando el control alcance cualquiera de los puntos especificados, el procesador pueda realizar el salto. La lógica resultante suele ser difícil de comprender, ya que no hay ninguna indicación cerca de un punto de salto que indique que el control saltará efectivamente. Es necesario analizar todo el programa para ver si alguna instrucción COMEFROM hace referencia a ese punto.

La semántica de una instrucción COMEFROM varía según el lenguaje de programación . En algunos lenguajes, el salto se produce antes de que se ejecute la instrucción en el punto especificado, y en otros, después. Dependiendo del lenguaje, varias instrucciones COMEFROM que hagan referencia al mismo punto pueden ser inválidas, no deterministas, ejecutarse en un orden determinado o inducir un procesamiento paralelo o concurrente, como se observa en Threaded Intercal .

COMEFROM apareció inicialmente en listas de instrucciones de lenguaje ensamblador de broma (como 'CMFRM'). Fue desarrollado en un artículo de Datamation de R. Lawrence Clark en 1973, [ 46 ] escrito en respuesta a la carta de Edsger Dijkstra titulada "La instrucción Go To se considera perjudicial" . COMEFROM finalmente se implementó en la variante C-INTERCAL del lenguaje de programación esotérico INTERCAL junto con el aún más oscuro 'COMEFROM calculado'. También hubo propuestas de Fortran [ 47 ] para 'COME FROM asignado' y una instrucción 'DONT' (para complementar el bucle 'DO' existente).

Salida anticipada basada en eventos de un bucle anidado

El modelo de Zahn fue propuesto en 1974, [ 48 ] y analizado en Knuth (1974) . Aquí se presenta una versión modificada.

salir cuando EventoA o EventoB o EventoC; xxx salidas EventoA: acciónA EventoB: acciónB EventoC: acciónC fin de salida ;

`exitwhen` se utiliza para especificar los eventos que pueden ocurrir dentro de `xxx` . Su ocurrencia se indica mediante el nombre del evento como una instrucción. Cuando ocurre un evento, se ejecuta la acción correspondiente y, a continuación, el control pasa a ` endexit` . Esta estructura proporciona una separación muy clara entre determinar que se aplica una situación y la acción que se debe tomar en consecuencia.

La función exitwhen es conceptualmente similar al manejo de excepciones , y en muchos lenguajes se utilizan excepciones o construcciones similares para este propósito.

El siguiente ejemplo sencillo consiste en buscar un elemento concreto en una tabla bidimensional.

salir cuando se encuentre o falte; para I := 1 a N hacer para J := 1 a M hacer si tabla[I,J] = objetivo entonces encontrado; desaparecido; salidas encontrado: imprimir ("el elemento está en la tabla"); falta: imprimir ("el elemento no está en la tabla"); fin de salida ;

Véase también

Referencias

  1. Payer, Mathias ; Kuznetsov, Volodymyr. "Sobre las diferencias entre las propiedades CFI, CPS y CPI" . nebelwelt.net . Consultado el 1 de junio de 2016 .
  2. "El descubrimiento de un fallo en Adobe Flash conduce a un nuevo método de mitigación de ataques" . Dark Reading . 10 de noviembre de 2015. Consultado el 1 de junio de 2016 .
  3. Endgame. "Endgame estará presente en Black Hat USA 2016" . www.prnewswire.com (Comunicado de prensa) . Consultado el 1 de junio de 2016 .
  4. "Bucles anidados en C con ejemplos" . GeeksforGeeks . 25/11/2019 . Consultado el 14/03/2024 .
  5. "Bucles anidados en Python" . www.w3schools.com . Consultado el 14 de marzo de 2024 .
  6. Dean, Jenna (22 de noviembre de 2019). "Bucles anidados" . The Startup . Recuperado el 14 de marzo de 2024 .
  7. Knuth, Donald E. (1974). "Programación estructurada con sentencias". Computing Surveys . 6 (4): 261– 301. CiteSeerX 10.1.1.103.6084 . doi : 10.1145/356635.356640 . S2CID 207630080 .go to  
  8. 1 2 3 4 5 Roberts, E. [1995] " Salidas de bucle y programación estructurada: reabrir el debate Archivado el 25-07-2014 en Wayback Machine ," Boletín ACM SIGCSE, (27)1: 268–272.
  9. "Definición de diccionario de bucle infinito" . Archivado del original el 1 de agosto de 2020. Consultado el 22 de enero de 2020 .
  10. "¿Qué es un bucle infinito (bucle sin fin)?" . Archivado del original el 15 de julio de 2019. Consultado el 22 de enero de 2020 .
  11. 1 2 "Condiciones de bucle desordenadas" . WikiWikiWeb . 3 de noviembre de 2014.
  12. 1 2 3 Knuth 1974 , p. 278, Iteraciones simples.
  13. Edsger W. Dijkstra , comunicación personal a Donald Knuth el 3 de enero de 1974, citado en Knuth (1974 , pág. 278, Simple Iterations) 
  14. 1 2 3 Knuth 1974 , pág. 279.
  15. "¿Qué es un bucle y cómo podemos usarlos?" . Archivado del original el 28-07-2020 . Consultado el 25-05-2020 .
  16. "rehacer - perldoc.perl.org" . perldoc.perl.org . Consultado el 25-09-2020 .
  17. "control_expressions - Documentación para Ruby 2.4.0" . docs.ruby-lang.org . Consultado el 25 de septiembre de 2020 .
  18. "control_expressions - Documentación para Ruby 2.3.0" . docs.ruby-lang.org . Consultado el 25 de septiembre de 2020 .
  19. Es una forma común de resolver elproblema del bucle y medio .
  20. Guía avanzada de scripting Bash: 11.3. Control de bucles
  21. Manual de PHP: " break "
  22. perldoc: último
  23. Lista de preguntas frecuentes de comp.lang.c · " Pregunta 20.20b "
  24. "named-loops" . open-std.org . 18 de septiembre de 2024.
  25. "Tecnología de la información — Lenguajes de programación — C" (PDF) . open-std.org . 4 de mayo de 2025.
  26. [Python-3000] Anuncio de PEP 3136 , Guido van Rossum
  27. 1 2 Kozen, Dexter (2008). "El teorema de Böhm-Jacopini es falso, proposicionalmente". Matemáticas de la construcción de programas (PDF) . Notas de clase en ciencias de la computación. Vol. 5133. págs. 177-192 . CiteSeerX 10.1.1.218.9241 . doi : 10.1007/978-3-540-70594-9_11 . ISBN    978-3-540-70593-2.
  28. Kosaraju, S. Rao. "Análisis de programas estructurados", Proc. Fifth Annual ACM Syrup. Theory of Computing, (mayo de 1973), 240-252; también en J. Computer and System Sciences, 9, 3 (diciembre de 1974), citado por Knuth (1974) .
  29. David Anthony Watt; William Findlay (2004). Conceptos de diseño de lenguajes de programación . John Wiley & Sons. págs. 215–221 . ISBN  978-0-470-85320-7.
  30. Dahl, Dijkstra y Hoare, "Programación estructurada", Academic Press, 1972.
  31. "6. Dejar que se vuelva loco" .
  32. "3.2.5.1 Estructuras de bucle" , Manual de referencia de GNU Bash , 18 de mayo de 2025
  33. "¿Cómo podría un lenguaje hacer que el bucle y medio sea menos propenso a errores?" . Stack Exchange: Diseño e implementación de lenguajes de programación .
  34. "3.2.4 Listas de comandos" , Manual de referencia de GNU Bash , 18 de mayo de 2025
  35. "¿Qué hace el operador coma ,?" . Stack Overflow .
  36. Meyer, Bertrand (1991). Eiffel: El lenguaje . Prentice Hall. pp. 129–131 . 
  37. "Macro LOOP de Common Lisp" .
  38. for_each . Sgi.com. Consultado el 09-11-2010.
  39. Capítulo 1. Boost.Foreach Archivado el 29/01/2010 en Wayback Machine . Boost-sandbox.sourceforge.net (19/12/2009). Consultado el 09/11/2010.
  40. ¿Cuál es la diferencia entre un iterador y un generador?
  41. Kiselyov, Oleg (enero de 2004). "Formas generales de recorrer colecciones en Scheme" .
  42. Anthony Ralston (2000). Enciclopedia de la informática . Nature Pub. Group. ISBN 978-1-56159-248-7. Consultado el 11 de mayo de 2013 .
  43. "¿Cómo diablos funciona async/await en Python 3.5?" . Tall, Snarky Canadian . 11/02/2016. Archivado del original el 10/01/2023 . Consultado el 10/01/2023 .
  44. Knuth, Donald Ervin (1997). Algoritmos fundamentales (PDF) . El arte de la programación informática. Vol. 1 (3.ª ed.). Addison-Wesley. Sección 1.4.5: Historia y bibliografía, págs. 229. ISBN    978-0-201-89683-1Archivado (PDF) del original el 21/10/2019 .
  45. Conway, Melvin E. (julio de 1963). "Diseño de un compilador de diagramas de transición separables" ( PDF) . Communications of the ACM . 6 (7). ACM: 396–408 . doi : 10.1145/366663.366704 . ISSN 0001-0782 . S2CID 10559786. Archivado (PDF) del original el 6 de abril de 2022. Recuperado el 21 de octubre de 2019 a través de la Biblioteca Digital de ACM.  
  46. Clarke, Lawrence , "No sabemos adónde ir si no sabemos de dónde venimos. Esta innovación lingüística cumple con todas las expectativas." , Datamation (artículo), archivado del original el 16 de julio de 2018 , consultado el 24 de septiembre de 2004..
  47. Modell, Howard; Slater, William (abril de 1978). "La programación estructurada considerada perjudicial" . ACM SIGPLAN Notices . 13 (4): 76– 79. doi : 10.1145/953411.953418 . Consultado el 18 de julio de 2014 .
  48. Zahn, CT "Una instrucción de control para la programación estructurada natural de arriba hacia abajo" presentada en el Simposio sobre Lenguajes de Programación, París, 1974.
  • Logotipo de Wikimedia CommonsContenido multimedia relacionado con el flujo de control en Wikimedia Commons.
  • Ir a la declaración considerada perjudicial
  • Una contribución lingüística de la programación sin GOTO
  • "Programación estructurada con instrucciones Go To" (PDF) . Archivado del original (PDF) el 24 de agosto de 2009. (2,88  MB)
  • "Manual del IBM 704" (PDF) . (31,4  MB)
Obtenido de " https://en.wikipedia.org/w/index.php?title=Control_flow&oldid=1358481513#continue-statement "