stringtranslate.com

Flujo de control

En informática , el flujo de control (o flujo de control ) es el orden en el que se ejecutan o evalúan declaraciones individuales , instrucciones o llamadas a funciones de un programa imperativo . El énfasis en el flujo de control explícito distingue un lenguaje de programación imperativo de un lenguaje de programación declarativo .

Dentro de un lenguaje de programación imperativo , una declaración de flujo de control es una declaración que da como resultado una elección sobre cuál de dos o más caminos seguir. Para los lenguajes funcionales no estrictos , existen funciones y construcciones de lenguaje para lograr el mismo resultado, pero generalmente no se denominan declaraciones de flujo de control.

Un conjunto de enunciados se estructura a su vez generalmente como un bloque , que además de agrupar, también define un ámbito léxico .

Las interrupciones y señales son mecanismos de bajo nivel que pueden alterar el flujo de control de una manera similar a una subrutina , pero generalmente ocurren como respuesta a algún estímulo o evento externo (que puede ocurrir de forma asincrónica ), en lugar de la ejecución de un proceso en línea. declaración de flujo de control.

A nivel de lenguaje de máquina o lenguaje ensamblador , las instrucciones de flujo de control generalmente funcionan alterando el contador del programa . Para algunas unidades centrales de procesamiento (CPU), las únicas instrucciones de flujo de control disponibles son instrucciones de bifurcación condicionales o incondicionales , también denominadas saltos.

Categorías

Un diagrama de estado de un proceso de búsqueda de mapeo de masa de iones peptídicos.

Los tipos de declaraciones de flujo de control admitidas por diferentes lenguajes varían, pero se pueden clasificar según su efecto:

Primitivos

Etiquetas

Una etiqueta es un nombre o número explícito asignado a una posición fija dentro del código fuente , y al que se puede hacer referencia mediante declaraciones de flujo de control que aparecen en otras partes del código fuente. Una etiqueta marca una posición dentro del código fuente y no tiene ningún otro efecto.

Los números de línea son una alternativa a una etiqueta con nombre utilizada en algunos idiomas (como BASIC ). Son números enteros colocados al comienzo de cada línea de texto en el código fuente. Los idiomas que los utilizan a menudo imponen la restricción de que los números de línea deben aumentar de valor en cada línea siguiente, pero es posible que no requieran que sean consecutivos. Por ejemplo, en BÁSICO:

10 DEJAR X = 3 20 IMPRIMIR X      

En otros lenguajes como C y Ada , una etiqueta es un identificador , que generalmente aparece al comienzo de una línea e inmediatamente seguido de dos puntos. Por ejemplo, en C:

Éxito : printf ( "La operación fue exitosa. \n " ); 

El lenguaje ALGOL 60 permitía tanto números enteros como identificadores como etiquetas (ambos unidos por dos puntos a la siguiente declaración), pero pocas o ninguna otra variante de ALGOL permitía números enteros. Los primeros compiladores de Fortran solo permitían números enteros como etiquetas. A partir de Fortran-90, también se permiten etiquetas alfanuméricas.

Ir a

La declaración goto (una combinación de las palabras en inglés go y to y pronunciadas en consecuencia) es la forma más básica de transferencia incondicional de control.

Aunque la palabra clave puede estar en mayúsculas o minúsculas según el idioma, normalmente se escribe como:

 ir a la  etiqueta

El efecto de una declaración goto es hacer que la siguiente declaración que se ejecute sea la declaración que aparece en (o inmediatamente después) de la etiqueta indicada.

Muchos científicos informáticos, en particular Dijkstra , han considerado dañinas las declaraciones Goto .

Subrutinas

La terminología para las subrutinas varía; Alternativamente, pueden conocerse como rutinas, procedimientos, funciones (especialmente si devuelven resultados) o métodos (especialmente si pertenecen a clases o clases de tipos ).

En la década de 1950, las memorias de las computadoras eran muy pequeñas según los estándares actuales, por lo que se usaban subrutinas principalmente [ cita necesaria ] para reducir el tamaño del programa. Un fragmento de código se escribió una vez y luego se usó muchas veces desde otros lugares de un programa.

Hoy en día, las subrutinas se utilizan con mayor frecuencia para ayudar a que un programa esté más estructurado, por ejemplo, aislando algún algoritmo u ocultando algún método de acceso a datos. Si muchos programadores trabajan en un programa, las subrutinas son un tipo de modularidad que puede ayudar a dividir el trabajo.

Secuencia

En la programación estructurada, la secuencia ordenada de comandos sucesivos se considera una de las estructuras de control básicas, que se utiliza como componente básico de los programas junto con la iteración, la recursividad y la elección.

Flujo de control estructurado mínimo

En mayo de 1966, Böhm y Jacopini publicaron un artículo [1] en Communications of the ACM que mostraba que cualquier programa con goto s podía transformarse en una forma libre de goto que implicaba únicamente elección (IF THEN ELSE) y bucles (WHILE condition DO xxx). ), posiblemente con código duplicado y/o la adición de variables booleanas (banderas de verdadero/falso). Autores posteriores demostraron que la elección puede ser reemplazada por bucles (y aún más variables booleanas).

Que tal minimalismo sea posible no significa que sea necesariamente deseable; después de todo, en teoría las computadoras sólo necesitan una instrucción de máquina (resta un número de otro y bifurcar si el resultado es negativo), pero las computadoras prácticas tienen docenas o incluso cientos de instrucciones de máquina.

Lo que mostró el artículo de Böhm y Jacopini fue que todos los programas podían ser libres de acceso. Otra investigación demostró que las estructuras de control con una entrada y una salida eran mucho más fáciles de entender que cualquier otra forma, [ cita necesaria ] principalmente porque podían usarse en cualquier lugar como una declaración sin interrumpir el flujo de control. En otras palabras, eran componibles . (Los desarrollos posteriores, como los lenguajes de programación no estrictos y, más recientemente, las transacciones de software componibles , han continuado con esta estrategia, haciendo que los componentes de los programas sean aún más componibles).

Algunos académicos adoptaron un enfoque purista del resultado de Böhm-Jacopini y argumentaron que incluso instrucciones como breaky returndesde el medio de los bucles son una mala práctica ya que no son necesarias en la prueba de Böhm-Jacopini y, por lo tanto, abogaron por que todos los bucles deberían tener un único punto de salida. Este enfoque purista está plasmado en el lenguaje Pascal (diseñado en 1968-1969), que hasta mediados de la década de 1990 era la herramienta preferida para enseñar introducción a la programación en el mundo académico. [2] La aplicación directa del teorema de Böhm-Jacopini puede dar lugar a la introducción de variables locales adicionales en el gráfico estructurado y también puede dar lugar a cierta duplicación de código . [3] Pascal se ve afectado por ambos problemas y, según estudios empíricos citados por Eric S. Roberts , los estudiantes de programación tuvieron dificultades para formular soluciones correctas en Pascal para varios problemas simples, incluida la escritura de una función para buscar un elemento en una matriz. Un estudio de 1980 de Henry Shapiro citado por Roberts encontró que usando sólo las estructuras de control proporcionadas por Pascal, la solución correcta fue dada por sólo el 20% de los sujetos, mientras que ningún sujeto escribió código incorrecto para este problema si se le permitía escribir una respuesta del medio de un bucle. [2]

Estructuras de control en la práctica.

La mayoría de los lenguajes de programación con estructuras de control tienen una palabra clave inicial que indica el tipo de estructura de control involucrada. [ se necesita aclaración ] Los idiomas luego se dividen en función de si las estructuras de control tienen o no una palabra clave final.

Elección

Declaraciones si-entonces (si no)

Las expresiones condicionales y las construcciones condicionales son características de un lenguaje de programación que realizan diferentes cálculos o acciones dependiendo de si una condición booleana especificada por el programador se evalúa como verdadera o falsa.

Las variaciones menos comunes incluyen:

Declaraciones de caso y cambio

Las declaraciones de cambio (o declaraciones de caso , o ramas multidireccionales ) comparan un valor dado con constantes específicas y toman medidas de acuerdo con la primera constante que coincida. Por lo general, existe una disposición para que se tome una acción predeterminada ("else", "de lo contrario") si ninguna coincidencia tiene éxito. Las declaraciones de cambio pueden permitir optimizaciones del compilador, como tablas de búsqueda . En los lenguajes dinámicos , los casos pueden no limitarse a expresiones constantes y pueden extenderse a la coincidencia de patrones , como en el ejemplo del script de shell de la derecha, donde *)implementa el caso predeterminado como un globo que coincide con cualquier cadena. La lógica de casos también se puede implementar en forma funcional, como en la declaración de SQLdecode .

Bucles

Un bucle es una secuencia de declaraciones que se especifica una vez pero que se puede ejecutar varias veces seguidas. El código "dentro" del bucle (el cuerpo del bucle, que se muestra a continuación como xxx ) se obedece un número específico de veces, o una vez para cada uno de una colección de elementos, o hasta que se cumpla alguna condición, o indefinidamente .

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

Bucles controlados por conteo

La mayoría de los lenguajes de programación tienen construcciones para repetir un bucle un número determinado de veces. En la mayoría de los casos, el conteo puede ir hacia abajo en lugar de hacia arriba y se pueden utilizar tamaños de paso distintos de 1.

En estos ejemplos, si N < 1, entonces el cuerpo del bucle puede ejecutarse una vez (donde I tiene el valor 1) o no ejecutarse en absoluto, según el lenguaje de programación.

En muchos lenguajes de programación, sólo se pueden utilizar de forma fiable números enteros en un bucle controlado por conteo. Los números de coma flotante se representan de manera imprecisa debido a restricciones de hardware, por lo que un bucle como

 para X := 0.1 paso 0.1 a 1.0 hacer

puede repetirse 9 o 10 veces, dependiendo de los errores de redondeo y/o del hardware y/o de la versión del compilador. Además, si el incremento de X se produce mediante sumas repetidas, los errores de redondeo acumulados pueden significar que el valor de X en cada iteración puede diferir de manera bastante significativa de la secuencia esperada 0,1, 0,2, 0,3,..., 1,0.

Bucles controlados por condición

La mayoría de los lenguajes de programación tienen construcciones para repetir un bucle hasta que cambie alguna condición. Algunas variaciones prueban la condición al inicio del ciclo; otros lo prueban al final. Si la prueba es al principio, el cuerpo puede omitirse por completo; si está al final, el cuerpo siempre se ejecuta al menos una vez.

Una interrupción de control es un método de detección de cambio de valor que se utiliza dentro de bucles ordinarios para activar el procesamiento de grupos de valores. Los valores se monitorean dentro del bucle y un cambio desvía el flujo del programa hacia el manejo del evento de grupo asociado con ellos.

 HACER HASTA (Fin del archivo) IF nuevo código postal <> código postal actual display_tally(código postal actual, recuento postal)  código postal actual = nuevo código postal recuento zip = 0 TERMINARA SI  cuenta zip++ BUCLE

Bucles controlados por colección

Varios lenguajes de programación (por ejemplo, Ada , D , C++11 , Smalltalk , PHP , Perl , Object Pascal , Java , C# , MATLAB , Visual Basic , Ruby , Python , JavaScript , Fortran 95 y posteriores) tienen construcciones especiales que permiten implícitamente recorrer todos los elementos de una matriz, o todos los miembros de un conjunto o colección.

 algunaColección hacer : [:cadaElemento |xxx]. para el artículo de la colección , comience xxx y finalice ; foreach (artículo; miColección) { xxx } foreach alguna matriz { xxx } foreach ($algunarray como $k => $v) { xxx } Colección<String> coll; para (Cadena s: coll) {} foreach ( cadena s en myStringCollection) { xxx } algunaColección | Para cada objeto { $_ } forall (índice = primero:último:paso...)

Scala tiene expresiones for , que generalizan los bucles controlados por colecciones y también admiten otros usos, como la programación asincrónica . Haskell tiene expresiones do y comprensiones, que juntas proporcionan una función similar a las expresiones for en Scala.

iteración general

Las construcciones de iteración generales, como forla declaración de C y la forma de Common Lispdo , se pueden utilizar para expresar cualquiera de los tipos de bucles anteriores, y otros, como recorrer un número determinado de colecciones en paralelo. Cuando se puede utilizar una construcción de bucle más específica, normalmente se prefiere a la construcción de iteración general, ya que a menudo aclara el propósito de la expresión.

Bucles infinitos

Los bucles infinitos se utilizan para asegurar que un segmento de programa se repita para siempre o hasta que surja una condición excepcional, como un error. Por ejemplo, un programa controlado por eventos (como un servidor ) debe repetirse indefinidamente, manejando los eventos a medida que ocurren y deteniéndose únicamente cuando un operador finaliza el proceso.

Se pueden implementar bucles infinitos utilizando otras construcciones de flujo de control. Lo más común es que en programación no estructurada se trate de un salto hacia atrás (ir a), mientras que en programación estructurada se trata de un bucle indefinido (bucle while) configurado para no terminar nunca, ya sea omitiendo la condición o estableciéndola explícitamente en verdadero, como while (true) .... Algunos lenguajes tienen construcciones especiales para bucles infinitos, normalmente omitiendo la condición de un bucle indefinido. Los ejemplos incluyen Ada ( loop ... end loop), [4] Fortran ( DO ... END DO), Go ( for { ... }) y Ruby ( loop do ... end).

A menudo, un bucle infinito se crea involuntariamente por un error de programación en un bucle controlado por condición, en el que la condición del bucle utiliza variables que nunca cambian dentro del bucle.

Continuación con la siguiente iteración.

A veces, dentro del cuerpo de un bucle existe el deseo de omitir el resto del cuerpo del bucle y continuar con la siguiente iteración del bucle. Algunos lenguajes proporcionan una declaración como continue(la mayoría de los lenguajes), skip[ 5] cycle (Fortran) o next(Perl y Ruby), que hará esto. El efecto es terminar prematuramente el cuerpo del bucle más interno y luego reanudarlo normalmente con la siguiente iteración. Si la iteración es la última del ciclo, el efecto es terminar todo el ciclo antes de tiempo.

Rehacer la iteración actual

Algunos lenguajes, como Perl [6] y Ruby, [7] tienen una redodeclaración que reinicia la iteración actual desde el principio.

Reiniciar bucle

Ruby tiene una retrydeclaración que reinicia todo el ciclo desde la iteración inicial. [8]

Salida anticipada de los bucles.

Cuando se utiliza un bucle controlado por conteo para buscar en una tabla, puede ser conveniente detener la búsqueda tan pronto como se encuentre el elemento requerido. Algunos lenguajes de programación proporcionan una declaración como break(la mayoría de los lenguajes), Exit(Visual Basic) o last(Perl), cuyo efecto es terminar el bucle actual inmediatamente y transferir el control a la declaración inmediatamente después de ese bucle. Otro término para los bucles de salida anticipada es bucle y medio .

El siguiente ejemplo se realiza en Ada , que admite tanto la salida temprana de los bucles como los bucles con prueba en el medio . Ambas características son muy similares y la comparación de ambos fragmentos de código mostrará la diferencia: la salida anticipada debe combinarse con una declaración if , mientras que una condición en el medio es una construcción autónoma.

con  Ada.Text  IO ; con  Ada.Integer  Texto  IO ;procedimiento  Print_Squares  es  X  :  Entero ; comenzar  Read_Data  :  bucle  Ada . Texto entero  IO . Obtener ( X ); salir de Read_Data cuando X = 0 ; Ada . Texto IO . Poner ( X * X ); Ada . Texto IO . Nueva línea ; finalizar el ciclo Read_Data ; finalizar Imprimir_Cuadrados ;                  

Python admite la ejecución condicional de código dependiendo de si se salió de un bucle antes (con una breakdeclaración) o no mediante el uso de una cláusula else con el bucle. Por ejemplo,

para  n  en  conjunto_de_números :  si  es primo ( n ):  imprimir ( "El conjunto contiene un número primo" )  romper else :  imprimir ( "El conjunto no contiene ningún número primo" )

La elsecláusula del ejemplo anterior está vinculada a la fordeclaración y no a la ifdeclaración interna. forTanto los bucles como los de Python whileadmiten dicha cláusula else, que se ejecuta sólo si no se ha producido una salida anticipada del bucle.

Algunos lenguajes admiten la ruptura de bucles anidados; En los círculos teóricos, esto se llama rupturas de varios niveles. 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 [9] y PHP, [10] o mediante saltos etiquetados (salir y continuar en una etiqueta determinada), como en Java y Perl. [11] Las alternativas a las rupturas multinivel incluyen rupturas individuales, junto con una variable de estado que se prueba para romper otro nivel; excepciones, que quedan atrapadas en el nivel al que se está desglosando; colocar los bucles anidados en una función y utilizar el retorno para efectuar la terminación de todo el bucle anidado; o usando una etiqueta y una declaración goto. C no incluye una ruptura multinivel y la alternativa habitual es utilizar un goto para implementar una ruptura etiquetada. [12] Python no tiene una interrupción o continuación multinivel; esto se propuso en PEP 3136 y se rechazó basándose en que la complejidad añadida no valía la pena el raro uso legítimo. [13]

La noción de rupturas de múltiples niveles tiene cierto interés en la informática teórica , porque da lugar a lo que hoy se llama jerarquía de Kosaraju . [14] En 1973, S. Rao Kosaraju refinó el teorema del programa estructurado demostrando que es posible evitar agregar variables adicionales en la programación estructurada, siempre que se permitan rupturas de bucles de varios niveles y profundidad arbitraria. [15] Además, Kosaraju demostró que existe una jerarquía estricta de programas: para cada número entero n , existe un programa que contiene una ruptura de varios niveles de profundidad n que no puede reescribirse como un programa con rupturas de múltiples niveles de profundidad menor que n . sin introducir variables añadidas. [14]

También se puede returnsalir de una subrutina ejecutando las declaraciones en bucle, rompiendo tanto el bucle anidado como la subrutina. Hay otras estructuras de control propuestas para pausas múltiples, pero generalmente se implementan como excepciones.

En su libro de texto de 2004, David Watt utiliza la noción de secuenciador de Tennent para explicar la similitud entre las pausas de varios niveles y las declaraciones de retorno. Watt señala que una clase de secuenciadores conocidos como secuenciadores de escape , definidos como "secuenciador que finaliza la ejecución de un comando o procedimiento que incluye texto", abarca tanto las interrupciones de los bucles (incluidas las interrupciones de varios niveles) como las declaraciones de retorno. Sin embargo, tal como se implementan comúnmente, los secuenciadores de retorno también pueden llevar un valor (de retorno), mientras que el secuenciador de interrupción implementado en los lenguajes contemporáneos generalmente no puede. [dieciséis]

Variantes e invariantes de bucle

Las variantes de bucle y las invariantes de bucle se utilizan para expresar la corrección de los bucles. [17]

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

Un invariante de bucle es una afirmación que debe ser verdadera antes de la primera iteración del bucle y seguir siendo verdadera después de cada iteración. Esto implica que cuando un bucle termina correctamente, se satisfacen tanto la condición de salida como el invariante del bucle. Los invariantes de bucle se utilizan para monitorear propiedades específicas de un bucle durante iteraciones sucesivas.

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

Sublenguaje de bucle

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

Tabla de referencias cruzadas del sistema de bucle

  1. a while (true) no cuenta como un bucle infinito para este propósito, porque no es una estructura de lenguaje dedicada.
  2. a b c d e f g h El bucle de C es una construcción de bucle general, no específicamente de conteo, aunque se usa a menudo para eso. for (init; test; increment)
  3. a b c Se pueden lograr rupturas profundas en APL, C, C++ y C# mediante el uso de etiquetas y gotos.
  4. Se agregó una iteración sobre objetos en PHP 5.
  5. a b c Se puede simular un bucle de conteo iterando sobre una lista incremental o un generador, por ejemplo, Python . range()
  6. a b c d e Se pueden lograr interrupciones profundas mediante el uso del manejo de excepciones.
  7. a No existe una construcción especial, ya que la whilefunción se puede utilizar para esto.
  8. a No existe una construcción especial, pero los usuarios pueden definir funciones de bucle generales.
  9. a El estándar C++ 11 introdujo el formato basado en rangos para . En STL , hay una función std::for_each de plantilla que puede iterar en contenedores STL y llamar a una función unaria para cada elemento. [19] La funcionalidad también se puede construir como macro en estos contenedores. [20]
  10. un bucle controlado por conteo se efectúa mediante iteración a lo largo de un intervalo entero; salida anticipada incluyendo una condición adicional para la salida.
  11. Eiffel admite una palabra reservada retry, sin embargo, se usa en el manejo de excepciones , no en el control de bucle.
  12. a Requiere el lenguaje de especificación de interfaz de comportamiento Java Modeling Language (JML).
  13. a Requiere que las variantes del bucle sean números enteros; Las variantes transfinitas no son compatibles. [1]
  14. a D admite colecciones infinitas y la capacidad de iterar sobre esas colecciones. Esto no requiere ninguna construcción especial.
  15. Se pueden lograr rupturas profundas utilizando procedimientosGO TO y.
  16. un Common Lisp es anterior al concepto de tipo de colección genérica.

Flujo de control estructurado no local

Muchos lenguajes de programación, especialmente aquellos que favorecen estilos de programación más dinámicos, ofrecen construcciones para flujo de control no local . Esto hace que el flujo de ejecución salte de un contexto determinado y se reanude en algún punto predeterminado. Condiciones , excepciones y continuaciones son tres tipos comunes de construcciones de control no local; También existen otros más exóticos, como generadores , corrutinas y la palabra clave async .

Condiciones

PL/I tiene unas 22 condiciones estándar (por ejemplo, ZERODIVIDE SUBSCRIPTRANGE ENDFILE) que pueden generarse y interceptarse mediante: acción de condición ON; Los programadores también pueden definir y utilizar sus propias condiciones con nombre.

Al igual que el if no estructurado , sólo se puede especificar una declaración, por lo que en muchos casos se necesita un GOTO para decidir dónde se debe reanudar 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 intentaron evitar el uso de condiciones.

Ejemplos de sintaxis comunes:

  Condición  ON etiqueta GOTO 

Excepciones

Los lenguajes modernos tienen una construcción estructurada especializada para el manejo de excepciones que no depende del uso de GOTOinterrupciones o retornos (multinivel). Por ejemplo, en C++ se puede escribir:

prueba { xxx1 // En algún lugar aquí xxx2 // usa: '''throw''' someValue; xxx3 } catch ( someClass & someId ) { // capturar valor de someClass actionForSomeClass } catch ( someType & anotherId ) { // capturar valor de someType actionForSomeType } catch (...) { // capturar cualquier cosa que no haya sido capturada actionForAnythingElse }                        

catchSe puede utilizar cualquier número y variedad de cláusulas anteriores. Si no hay ninguna catchcoincidencia en particular throw, el control se filtra a través de llamadas a subrutinas y/o bloques anidados hasta que catchse encuentra una coincidencia o hasta que se alcanza el final del programa principal, momento en el cual el programa se detiene por la fuerza con un mensaje de error adecuado.

A través de la influencia de C++, catches la palabra clave reservada para declarar un controlador de excepciones de coincidencia de patrones en otros lenguajes populares hoy en día, como Java o C#. Algunos otros lenguajes como Ada usan la palabra clave exceptionpara introducir un controlador de excepciones y luego pueden incluso emplear una palabra clave diferente ( whenen Ada) para la coincidencia de patrones. Algunos lenguajes como AppleScript incorporan marcadores de posición en la sintaxis del controlador de excepciones para extraer automáticamente varios datos cuando ocurre la excepción. Este enfoque se ejemplifica a continuación con la on errorconstrucción de AppleScript:

Intente  establecer  myNumber  en  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" ) y luego muestre el cuadro de diálogo "No debe hacer eso" finalice el intento           

El libro de texto de David Watt de 2004 también analiza el manejo de excepciones en el marco de secuenciadores (presentado en este artículo en la sección sobre salidas tempranas de bucles). Watt señala que una situación anormal, generalmente ejemplificada con desbordamientos aritméticos o fallas de entrada/salida como archivo no encontrado, es un tipo de error que "se detecta en alguna unidad de programa de bajo nivel, pero [para la cual] se ubica de manera más natural un controlador". en una unidad de programa de alto nivel". Por ejemplo, un programa puede contener varias llamadas para leer archivos, pero la acción a realizar cuando no se encuentra un archivo depende del significado (propósito) del archivo en cuestión para el programa y, por lo tanto, no se puede crear una rutina de manejo para esta situación anormal. ubicado en el código del sistema de bajo nivel. Watts señala además que la introducción de pruebas de indicadores de estado en la persona que llama, como implicaría la programación estructurada de salida única o incluso secuenciadores de retorno (salidas múltiples), da como resultado una situación en la que "el código de la aplicación tiende a saturarse con pruebas de indicadores de estado" y que "el programador podría, por olvido o por pereza, omitir probar un indicador de estado. De hecho, las situaciones anormales representadas por indicadores de estado se ignoran de forma predeterminada". Watt señala que, a diferencia de las pruebas de indicadores de estado, las excepciones tienen el comportamiento predeterminado opuesto , lo que hace que el programa finalice a menos que trate la excepción explícitamente de alguna manera, posiblemente agregando código explícito para ignorarla. Basándose en estos argumentos, Watt concluye que los secuenciadores de salto o los secuenciadores de escape son menos adecuados como secuenciador de excepción dedicado con la semántica analizada anteriormente. [21]

En Object Pascal, D, Java, C# y Python finallyse puede agregar una cláusula a la tryconstrucción. No importa cómo salga el control, se garantiza que tryel código dentro de la cláusula se ejecutará. finallyEsto es útil cuando se escribe código que debe renunciar a un recurso costoso (como un archivo abierto o una conexión de base de datos) cuando finaliza el procesamiento:

FileStream stm = nulo ; // Ejemplo de C# try { stm = new FileStream ( "logfile.txt" , FileMode . Create ); devolver ProcessStuff ( stm ); // puede generar una excepción } finalmente { if ( stm != null ) stm . Cerca (); }                  

Dado que este patrón es bastante común, C# tiene una sintaxis especial:

usando ( var stm = new FileStream ( "logfile.txt" , FileMode . Create )) { return ProcessStuff ( stm ); // puede generar una excepción }         

Al salir del usingbloque, el compilador garantiza que el stmobjeto se libera, vinculando efectivamente la variable al flujo del archivo mientras se abstrae de los efectos secundarios de inicializar y liberar el archivo. La declaración de Python withy el argumento de bloque de Ruby File.opense utilizan con un efecto similar.

Todos los lenguajes mencionados anteriormente definen excepciones estándar y las circunstancias bajo las cuales se producen. Los usuarios pueden lanzar sus propias excepciones; C++ permite a los usuarios lanzar y capturar casi cualquier tipo, incluidos tipos básicos como int, mientras que otros lenguajes como Java son menos permisivos.

Continuaciones

asíncrono

C# 5.0 introdujo la palabra clave async para admitir E/S asíncronas en un "estilo directo".

Generadores

Los generadores , también conocidos como semicorutinas, permiten ceder el control temporalmente a un método de consumidor, normalmente utilizando una yieldpalabra clave (descripción de rendimiento). Al igual que la palabra clave async, esto admite la programación en un "estilo directo".

Corrutinas

Las corrutinas son funciones que pueden ceder el control entre sí: una forma de multitarea cooperativa sin subprocesos.

Las corrutinas se pueden implementar como una biblioteca si el lenguaje de programación proporciona continuaciones o generadores, por lo que la distinción entre corrutinas y generadores en la práctica es un detalle técnico.

Referencia cruzada de flujo de control no local

Estructuras de control propuestas

En un artículo falso de Datamation [28] en 1973, R. Lawrence Clark sugirió que la declaración GOTO podría reemplazarse por la declaración COMEFROM y proporciona algunos ejemplos entretenidos. COMEFROM se implementó en un lenguaje de programación esotérico llamado INTERCAL .

El artículo de Donald Knuth de 1974 "Programación estructurada con declaraciones go to", [29] identifica dos situaciones que no estaban cubiertas por las estructuras de control enumeradas anteriormente y dio ejemplos de estructuras de control que podrían manejar estas situaciones. A pesar de su utilidad, estas construcciones aún no han llegado a los lenguajes de programación convencionales.

Bucle con prueba en el medio.

Dahl propuso lo siguiente en 1972: [30]

 bucle  bucle xxx1 leer(char); mientras prueba; mientras  no esté en EndOfFile; xxx2 escribir(char); repetir ; repetir ;

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 idiomas. Si se omite while , obtenemos un bucle infinito. La construcción aquí se puede considerar como un bucle do con el control while en el medio. Por tanto, esta única construcción puede reemplazar varias construcciones en la mayoría de los lenguajes de programación.

Los lenguajes que carecen de esta construcción generalmente la emulan usando un modismo equivalente de bucle infinito con interrupción:

mientras (verdadero) { xxx1 si ( no prueba) se rompe xxx2}

Una posible variante es permitir más de una prueba while ; dentro del bucle, pero el uso de exitwhen (ver la siguiente sección) parece cubrir mejor este caso.

En Ada , la construcción de bucle anterior ( loop - while - repetir ) se puede representar usando un bucle infinito estándar ( loop - end loop ) que tiene una cláusula exit when en el medio (no debe confundirse con la declaración exitwhen en la siguiente sección ).

con  Ada.Text_IO ; con  Ada.Integer_Text_IO ;procedimiento  Print_Squares  es  X  :  Entero ; comenzar  Read_Data  :  bucle  Ada . Entero_Texto_IO . Obtener ( X );  salir  de Read_Data  cuando  X  =  0 ;  Ada . Texto  IO . Poner  ( X  *  X );  Ada . Texto  IO . Nueva línea ;  finalizar  el ciclo  Read_Data ; finalizar  Imprimir_Cuadrados ;

Nombrar un bucle (como Read_Data en este ejemplo) es opcional pero permite dejar el bucle externo de varios bucles anidados.

Múltiples salidas anticipadas/salidas de bucles anidados

Esta construcción fue propuesta por Zahn en 1974. [31] Aquí se presenta una versión modificada.

 salir cuando EventA o EventB o EventC; xxx salidas EventoA: acciónA EventoB: acciónB EventoC: acciónC endexitar ;

exitwhen se usa para especificar los eventos que pueden ocurrir dentro de xxx , su ocurrencia se indica usando el nombre del evento como declaración. Cuando ocurre algún evento, se lleva a cabo la acción relevante y luego el control pasa justo después de endexit . Esta construcción proporciona una separación muy clara entre determinar que se aplica alguna situación y la acción que se debe tomar para esa situació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 simple implica buscar en una tabla bidimensional un elemento en particular.

 salir cuando se encuentre o desaparezca; 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"); endexitar ;

Seguridad

Una forma de atacar una pieza de software es redirigir el flujo de ejecución de un programa. Para defenderse contra estos ataques se utiliza una variedad de técnicas de integridad del flujo de control , incluidos los valores controlados de pila , la protección contra desbordamiento del búfer , las pilas ocultas y la verificación del puntero vtable . [32] [33] [34]

Ver también

Referencias

  1. ^ Böhm, Jacopini. "Diagramas de flujo, máquinas de Turing y lenguajes con sólo dos reglas de formación" Comm. ACM , 9(5):366-371, mayo de 1966.
  2. ^ ab Roberts, E. [1995] "Salidas de bucle y programación estructurada: reapertura del debate Archivado el 25 de julio de 2014 en Wayback Machine ", Boletín ACM SIGCSE, (27) 1: 268–272.
  3. ^ David Anthony Watt; William Findlay (2004). Conceptos de diseño de lenguajes de programación . John Wiley e hijos. pag. 228.ISBN _ 978-0-470-85320-7.
  4. ^ Programación Ada: Control: Bucle sin fin
  5. ^ "¿Qué es un bucle y cómo podemos utilizarlo?". Archivado desde el original el 28 de julio de 2020 . Consultado el 25 de mayo de 2020 .
  6. ^ "rehacer - perldoc.perl.org". perldoc.perl.org . Consultado el 25 de septiembre de 2020 .
  7. ^ "control_expressions - Documentación para Ruby 2.4.0". docs.ruby-lang.org . Consultado el 25 de septiembre de 2020 .
  8. ^ "control_expressions - Documentación para Ruby 2.3.0". docs.ruby-lang.org . Consultado el 25 de septiembre de 2020 .
  9. ^ Guía avanzada de secuencias de comandos Bash: 11.3. Control de bucle
  10. ^ Manual de PHP: "romper"
  11. ^ perldoc: último
  12. ^ Lista de preguntas frecuentes de comp.lang.c · "Pregunta 20.20b"
  13. ^ [Python-3000] Anuncio de PEP 3136, Guido van Rossum
  14. ^ ab Kozen, Dexter (2008). "El teorema de Böhm-Jacopini es falso, proposicionalmente". Matemáticas de la construcción de programas (PDF) . Apuntes de conferencias sobre informática. 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.
  15. ^ Kosaraju, S. Rao. "Análisis de programas estructurados", Proc. Quinto Jarabe Anual de ACM. Teoría de la Computación, (mayo de 1973), 240-252; también en J. Computer and System Sciences, 9, 3 (diciembre de 1974). citado por Knuth, Donald (1974). "Programación estructurada con declaraciones go to". Encuestas Informáticas . 6 (4): 261–301. CiteSeerX 10.1.1.103.6084 . doi :10.1145/356635.356640. S2CID  207630080. 
  16. ^ David Anthony Watt; William Findlay (2004). Conceptos de diseño de lenguajes de programación . John Wiley e hijos. págs. 215-221. ISBN 978-0-470-85320-7.
  17. ^ Meyer, Bertrand (1991). Eiffel: el idioma . Prentice Hall. págs. 129-131.
  18. ^ "Macro de LOOP Lisp común".
  19. ^ para_cada uno. Sgi.com. Recuperado el 9 de noviembre de 2010.
  20. Capítulo 1. Boost.Foreach Archivado el 29 de enero de 2010 en Wayback Machine . Boost-sandbox.sourceforge.net (19 de diciembre de 2009). Recuperado el 9 de noviembre de 2010.
  21. ^ David Anthony Watt; William Findlay (2004). Conceptos de diseño de lenguajes de programación . John Wiley e hijos. págs. 221–222. ISBN 978-0-470-85320-7.
  22. ^ "Asyncio: E/S asincrónica: documentación de Python 3.10.2".
  23. ^ "Sockets/Async". GitHub . 25 de febrero de 2022.
  24. ^ "Generadores: el libro inestable de Rust".
  25. ^ "Corona - Óxido".
  26. ^ "Primeros pasos: programación asincrónica en Rust".
  27. ^ "Encuentro de Jitsi". Storm-enroute.com . Consultado el 7 de septiembre de 2022 .
  28. ^ No sabemos adónde IR si no sabemos de dónde venimos. Esta innovación lingüística (parodia) está a la altura de todas las expectativas. Archivado el 16 de julio de 2018 en Wayback Machine. Por R. Lawrence Clark* De Datamation, diciembre de 1973
  29. ^ Knuth, Donald E. "Programación estructurada con declaraciones de acceso" ACM Computing Surveys 6(4):261-301, diciembre de 1974.
  30. ^ Dahl & Dijkstra & Hoare, Academic Press "Programación estructurada", 1972.
  31. ^ Zahn, CT "Una declaració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.
  32. ^ Pagador, Mathias ; Kuznetsov, Volodymyr. "Sobre las diferencias entre las propiedades CFI, CPS y CPI". nebelwelt.net . Consultado el 1 de junio de 2016 .
  33. ^ "El descubrimiento de errores de Adobe Flash conduce a un nuevo método de mitigación de ataques". Lectura oscura . 10 de noviembre de 2015 . Consultado el 1 de junio de 2016 .
  34. ^ Fin del juego. "Endgame se presentará en Black Hat USA 2016". www.prnewswire.com . Consultado el 1 de junio de 2016 .

Otras lecturas

enlaces externos