Articulo de referencia

Estrategia de evaluación

En un lenguaje de programación , una estrategia de evaluación es un conjunto de reglas para evaluar expresiones . [ 1 ] El término se usa a menudo para referirse a la noción más...

En un lenguaje de programación , una estrategia de evaluación es un conjunto de reglas para evaluar expresiones . [ 1 ] El término se usa a menudo para referirse a la noción más específica de una estrategia de paso de parámetros [ 2 ] que define el tipo de valor que se pasa a la función para cada parámetro (la estrategia de enlace ) [ 3 ] y si se deben evaluar los parámetros de una llamada a función, y en caso afirmativo en qué orden (el orden de evaluación ). [ 4 ] La noción de estrategia de reducción es distinta, [ 5 ] aunque algunos autores confunden los dos términos y la definición de cada término no está ampliamente aceptada. [ 6 ] La estrategia de evaluación de un lenguaje de programación es parte de su semántica de alto nivel . Algunos lenguajes, como PureScript , tienen variantes con diferentes estrategias de evaluación. Algunos lenguajes declarativos , como Datalog , admiten múltiples estrategias de evaluación.

Al igual que en matemáticas, la evaluación es el proceso de encontrar el valor correspondiente a una expresión. [ 7 ] [ 8 ]

La convención de llamada consiste en los detalles de bajo nivel específicos de la plataforma para el paso de parámetros.

Ejemplo

Para ilustrarlo, al ejecutar una llamada a una función, f(a, b)primero se pueden evaluar los argumentos ay b, almacenar los resultados en referencias o ubicaciones de memoria ref_ay ref_b, y luego evaluar el cuerpo de la función con esas referencias pasadas. Esto le da a la función la capacidad de buscar los valores de los argumentos originales pasados ​​mediante la desreferenciación de los parámetros (algunos lenguajes usan operadores específicos para realizar esto), modificarlos mediante asignación como si fueran variables locales y devolver valores a través de las referencias. Esta es la estrategia de evaluación por referencia. [ 9 ]

Mesa

Esta es una tabla de estrategias de evaluación e idiomas representativos por año de introducción. Los idiomas representativos se enumeran en orden cronológico, comenzando con el idioma o los idiomas que introdujeron la estrategia y seguidos de los idiomas más importantes que la utilizan. [ 10 ] : 434

Órdenes de evaluación

Mientras que el orden de las operaciones define el árbol de sintaxis abstracta de la expresión, el orden de evaluación define el orden en que se evalúan las expresiones. Por ejemplo, el programa Python

def f ( x ): print ( x , end = "" ) return ximprimir ( f ( 1 ) + f ( 2 ))

resultados 123debido al orden de evaluación de izquierda a derecha de Python, pero un programa similar en OCaml :

let f x = print_int x ; x ;; print_int ( f 1 + f 2 )

resultados 213debido al orden de evaluación de derecha a izquierda de OCaml.

El orden de evaluación es visible principalmente en el código con efectos secundarios , pero también afecta el rendimiento del código porque un orden rígido inhibe la planificación de instrucciones . Por esta razón, los estándares de lenguaje como C++ tradicionalmente no especificaban el orden, aunque lenguajes como Java y C# definen el orden de evaluación de izquierda a derecha [ 10 ] : 240–241 y el estándar C++17 ha añadido restricciones al orden de evaluación. [ 24 ]

Evaluación estricta

El orden aplicativo es una familia de órdenes de evaluación en la que los argumentos de una función se evalúan completamente antes de que se aplique la función. [ 25 ] Esto tiene el efecto de hacer que la función sea estricta , es decir, el resultado de la función es indefinido si alguno de los argumentos es indefinido, por lo que la evaluación en orden aplicativo se denomina más comúnmente evaluación estricta . Además, una llamada a función se realiza tan pronto como se encuentra en un procedimiento, por lo que también se denomina evaluación ansiosa o evaluación codiciosa . [ 26 ] [ 27 ] Algunos autores se refieren a la evaluación estricta como "llamada por valor" debido a que la estrategia de enlace de llamada por valor requiere evaluación estricta. [ 4 ]

Common Lisp , Eiffel y Java evalúan los argumentos de las funciones de izquierda a derecha. C deja el orden indefinido. [ 28 ] Scheme requiere que el orden de ejecución sea la ejecución secuencial de una permutación no especificada de los argumentos. [ 29 ] OCaml también deja el orden indefinido, pero en la práctica evalúa los argumentos de derecha a izquierda debido al diseño de su máquina abstracta . [ 30 ] Todas estas son evaluaciones estrictas.

Evaluación no estricta

Un orden de evaluación no estricto es un orden de evaluación que no es estricto; es decir, una función puede devolver un resultado antes de que todos sus argumentos se hayan evaluado completamente. [ 31 ] : 46–47 El ejemplo prototípico es la evaluación en orden normal , que no evalúa ninguno de los argumentos hasta que se necesitan en el cuerpo de la función. [ 32 ] La evaluación en orden normal tiene la propiedad de que termina sin error siempre que cualquier otro orden de evaluación hubiera terminado sin error. [ 33 ] El nombre "orden normal" proviene del cálculo lambda, donde la reducción en orden normal encontrará una forma normal si existe (es una estrategia de reducción "normalizadora" ). [ 34 ] La evaluación perezosa se clasifica en este artículo como una técnica de enlace en lugar de un orden de evaluación. Pero esta distinción no siempre se sigue y algunos autores definen la evaluación perezosa como evaluación en orden normal o viceversa, [ 25 ] [ 35 ] o confunden la no estrictez con la evaluación perezosa. [ 31 ] : 43–44

Las expresiones booleanas en muchos lenguajes utilizan una forma de evaluación no estricta llamada evaluación de cortocircuito , donde la evaluación evalúa la expresión de la izquierda pero puede omitir la de la derecha si el resultado se puede determinar; por ejemplo, en una expresión disyuntiva (OR) donde truese encuentra, o en una expresión conjuntiva (AND) donde falsese encuentra, y así sucesivamente. [ 35 ] Las expresiones condicionales también utilizan una evaluación no estricta: solo se evalúa una de las ramas. [ 31 ]

Comparación de la evaluación de orden aplicativo y de orden normal

Con la evaluación de orden normal, las expresiones que contienen un cálculo costoso, un error o un bucle infinito se ignorarán si no son necesarias, [ 4 ] lo que permite la especificación de construcciones de flujo de control definidas por el usuario, una funcionalidad que no está disponible con la evaluación de orden aplicativo. La evaluación de orden normal utiliza estructuras complejas como thunks para expresiones no evaluadas, en comparación con la pila de llamadas utilizada en la evaluación de orden aplicativo. [ 36 ] Históricamente, la evaluación de orden normal ha carecido de herramientas de depuración útiles debido a su complejidad. [ 37 ]

Estrategias de unión estricta

Llamar por valor

En la llamada por valor (o paso por valor), el valor evaluado de la expresión del argumento se vincula a la variable correspondiente en la función (frecuentemente copiando el valor en una nueva región de memoria). Si la función o procedimiento puede asignar valores a sus parámetros, solo se asigna su variable local ; es decir, todo lo que se pasa a una llamada a la función permanece sin cambios en el ámbito del llamador cuando la función regresa. Por ejemplo, en Pascal , pasar un array por valor hará que se copie todo el array, y cualquier modificación a este array será invisible para el llamador: [ 38 ]

programa principal ; usa crt ;procedimiento ImprimirArray ( a : Array de enteros ) ; var i : Entero ; inicio para i := Bajo ( a ) a Alto ( a ) hacer Escribir ( a [ i ]) ; EscribirLn () ; fin ;Procedimiento Modificar ( Fila : Matriz de enteros ) ; inicio ImprimirMatriz ( Fila ) ; // 123 Fila [ 1 ] := 4 ; ImprimirMatriz ( Fila ) ; // 143 fin ;Var A : Arreglo de enteros ; inicio A := [ 1 , 2 , 3 ] ; ImprimirArreglo ( A ) ; // 123 Modificar ( A ) ; ImprimirArreglo ( A ) ; // 123 fin .

Desviación semántica

Estrictamente hablando, en el paso por valor, ninguna operación realizada por la rutina llamada es visible para quien la llama, salvo como parte del valor de retorno. [ 19 ] Esto implica una forma de programación puramente funcional en la semántica de implementación. Sin embargo, la expresión "paso por valor donde el valor es una referencia" se ha vuelto común en algunos lenguajes, por ejemplo, en la comunidad Java. [ 39 ] En comparación con el paso por valor tradicional, el valor que se pasa no es un valor en el sentido ordinario de valor, como un entero que se puede escribir como un literal, sino un identificador de referencia interno de la implementación . Las modificaciones a este identificador de referencia son visibles para quien la llama. Debido a la modificación visible, esta forma de "paso por valor" se denomina más apropiadamente paso por compartición . [ 19 ]

En los lenguajes puramente funcionales , los valores y las estructuras de datos son inmutables, por lo que una función no puede modificar ninguno de sus argumentos. Por lo tanto, normalmente no existe diferencia semántica entre pasar por valor y pasar por referencia o un puntero a la estructura de datos , y las implementaciones suelen utilizar internamente la llamada por referencia para obtener ventajas en eficiencia. No obstante, estos lenguajes se suelen describir como lenguajes de llamada por valor.

Llamar por referencia

La llamada por referencia (o paso por referencia) es una estrategia de evaluación en la que un parámetro se vincula a una referencia implícita a la variable utilizada como argumento, en lugar de a una copia de su valor. Esto generalmente significa que la función puede modificar (es decir, asignar un valor a ) la variable utilizada como argumento, algo que será visible para quien la llama. Por lo tanto, la llamada por referencia puede utilizarse para proporcionar un canal de comunicación adicional entre la función llamada y la función que la llama. El paso por referencia puede mejorar significativamente el rendimiento: al llamar a una función con una estructura de varios megabytes como argumento, no es necesario copiar la estructura completa, sino solo la referencia a la misma (que generalmente es una palabra de máquina y ocupa solo unos pocos bytes). Sin embargo, un lenguaje con llamada por referencia dificulta que el programador rastree los efectos de una llamada a función y puede introducir errores sutiles.

Debido a la variación en la sintaxis, la diferencia entre llamada por referencia (donde el tipo de referencia es implícito) y llamada por compartición (donde el tipo de referencia es explícito) a menudo no queda clara a primera vista. Una prueba sencilla consiste en comprobar si es posible escribir una swap(a, b)función tradicional en el lenguaje. [ 39 ] Por ejemplo, en Fortran:

programa Principal implícito ninguno entero :: a = 1 entero :: b = 2 llamar Intercambiar ( a , b ) imprimir * , a , b ! 2 1 contiene  subrutina Intercambiar ( a , b ) entero , intención ( entrada salida ) :: a , b entero :: temp temp = a a = b b = temp fin de la subrutina Intercambiar fin del programa Principal

Por lo tanto, la intención de Fortran inoutimplementa el paso por referencia; cualquier variable puede convertirse implícitamente en un identificador de referencia. En contraste, lo más parecido que se puede obtener en Java es:

public class Main { static class Box { int value ; public Box ( int value ) { this . value = value ; } }static void swap ( Caja a , Caja b ) { int temp = a . valor ; a . valor = b . valor ; b . valor = temp ; }public static void main ( String [] args ) { Box a = new Box ( 1 ); Box b = new Box ( 2 ); swap ( a , b ); System . out . printf ( "a = %d, b = %d%n" , a . value , b . value ); // salida: a = 2, b = 1 } }

donde se debe usar un tipo explícito Boxpara introducir un identificador. Java es de paso por compartición pero no de paso por referencia. [ 39 ]

Llamar mediante copiar y restaurar

La llamada por copia y restauración —también conocida como "copia de entrada y copia de salida", "llamada por valor de resultado" o "llamada por valor de retorno" (como se denomina en la comunidad Fortran )— es una variación de la llamada por referencia. Con la llamada por copia y restauración, el contenido del argumento se copia a una nueva variable local a la invocación de la llamada. La función puede modificar esta variable, de forma similar a la llamada por referencia, pero como la variable es local, las modificaciones no son visibles fuera de la invocación de la llamada durante la misma. Cuando la llamada a la función finaliza, el contenido actualizado de esta variable se copia de nuevo para sobrescribir el argumento original ("restaurado"). [ 40 ]

La semántica de la llamada por copia-restauración es similar en muchos casos a la llamada por referencia, pero difiere cuando dos o más argumentos de función se aliasan entre sí (es decir, apuntan a la misma variable en el entorno del llamador). En la llamada por referencia, escribir en un argumento afectará al otro durante la ejecución de la función. En la llamada por copia-restauración, escribir en un argumento no afectará al otro durante la ejecución de la función, pero al final de la llamada, los valores de los dos argumentos pueden diferir, y no está claro qué argumento se copia primero y, por lo tanto, qué valor recibe la variable del llamador. [ 41 ] Por ejemplo, Ada especifica que la asignación de copia para cada in outparámetro outse produce en un orden arbitrario. [ 42 ] Del siguiente programa (ilegal en Ada 2012) [ 43 ] se puede ver que el comportamiento de GNAT es copiar en orden de izquierda a derecha al regresar:

con Ada.Text_IO ; usar Ada.Text_IO ;El procedimiento Test_Copy_Restore es el procedimiento Modify ( A , B : in out Integer ) es begin A := A + 1 ; B := B + 2 ; end Modify ; X : Integer := 0 ; begin Modify ( X , X ); Put_Line ( "X = " & Integer ' Image ( X )); end Test_Copy_Restore ; -- $ gnatmake -gnatd.E test_copy_restore.adb; ./test_copy_restore -- test_copy_restore.adb:12:10: advertencia: el valor actual editable para "A" se superpone con el valor actual para "B" [-gnatw.i] -- X = 2

Si el programa devolviera 1, estaría copiando de derecha a izquierda, y bajo la semántica de llamada por referencia, el programa devolvería 3.

Cuando la referencia se pasa al llamador sin inicializar (por ejemplo, un outparámetro en Ada en lugar de un in outparámetro), esta estrategia de evaluación puede denominarse "llamada por resultado".

Esta estrategia ha ganado atención en el multiprocesamiento y las llamadas a procedimientos remotos , [ 44 ] ya que, a diferencia de la llamada por referencia, no requiere una comunicación frecuente entre los hilos de ejecución para el acceso a las variables.

Llama compartiendo

La llamada por compartición (también conocida como "paso por compartición", "llamada por objeto" o "llamada por compartición de objetos") es una estrategia de evaluación intermedia entre la llamada por valor y la llamada por referencia. En lugar de exponer todas las variables como referencias, solo una clase específica de valores, denominados "referencias", " tipos encapsulados " u "objetos", tienen semántica de referencia, y son las direcciones de estos punteros las que se pasan a la función. Al igual que en la llamada por valor, el valor de la dirección pasada es una copia, y la asignación directa al parámetro de la función sobrescribe la copia y no es visible para la función que realiza la llamada. Al igual que en la llamada por referencia, la modificación del destino del puntero es visible para la función que realiza la llamada. Las modificaciones de un objeto mutable dentro de la función son visibles para quien realiza la llamada porque el objeto no se copia ni se clona, ​​sino que se comparte ; de ​​ahí el nombre "llamada por compartición". [ 19 ]

La técnica fue observada por primera vez por Barbara Liskov en 1974 para el lenguaje CLU . [ 19 ] Es utilizada por muchos lenguajes modernos como Python (los valores compartidos se denominan "objetos"), [ 45 ] Java (objetos), Ruby (objetos), JavaScript (objetos), Scheme (estructuras de datos como vectores), [ 46 ] AppleScript (listas, registros, fechas y objetos de script), OCaml y ML (referencias, registros, matrices, objetos y otros tipos de datos compuestos), Maple (rtables y tablas) y Tcl (objetos). [ 47 ] El término "llamada por compartición", tal como se usa en este artículo, no es de uso común; la terminología es inconsistente en diferentes fuentes. Por ejemplo, en la comunidad Java, dicen que Java es llamada por valor. [ 39 ]

Para objetos inmutables , no existe una diferencia real entre la llamada por compartición y la llamada por valor, excepto si la identidad del objeto es visible en el lenguaje. El uso de la llamada por compartición con objetos mutables es una alternativa a los parámetros de entrada/salida : el parámetro no se asigna (el argumento no se sobrescribe y la identidad del objeto no cambia), pero el objeto (argumento) se modifica. [ 48 ]

Por ejemplo, en Python, las listas son mutables y se pasan mediante compartición, por lo que:

def f ( l : lista [ int ]) -> Ninguno : l . append ( 1 )m : lista [ int ] = [] f ( m ) imprimir ( m )

Las salidas se producen [1]porque el appendmétodo modifica el objeto sobre el que se llama.

Por el contrario, las asignaciones dentro de una función no son perceptibles para quien la llama. Por ejemplo, este código vincula el argumento formal a un nuevo objeto, pero no es visible para quien la llama porque no lo modifica a_list:

def f ( l : lista [ int ]) -> Ninguno : l = l + [ 1 ] print ( l ) # [1]m : lista [ int ] = [] f ( m ) imprimir ( m ) # []

Llamar por dirección

La llamada por dirección , el paso por dirección o la llamada/paso por puntero es un método de paso de parámetros donde la dirección del argumento se pasa como parámetro formal. Dentro de la función, la dirección (puntero) se puede usar para acceder o modificar el valor del argumento. Por ejemplo, la operación de intercambio se puede implementar de la siguiente manera en C: [ 49 ]

#include <stdio.h>void swap ( int * a , int * b ) { int temp = * a ; * a = * b ; * b = temp ; }int main () { int a = 1 ; int b = 2 ; swap ( &a a , & b ); printf ( "%d %d \n " , a , b ); // 2 1 return 0 ; }

Algunos autores tratan &como parte de la sintaxis de llamada swap. Según esta visión, C admite la estrategia de paso de parámetros por referencia. [ 50 ] Otros autores adoptan una visión diferente, según la cual la implementación presentada swapen C es solo una simulación de paso por referencia usando punteros. [ 51 ] Según esta visión de "simulación", las variables mutables en C no son de primera clase (es decir, los l-valores no son expresiones), sino que lo son los tipos de puntero. En esta visión, el programa de intercambio presentado es azúcar sintáctico para un programa que usa punteros en todo momento, [ 52 ] por ejemplo este programa ( ready assignse han añadido para resaltar las similitudes con el Boxprograma de paso por compartición de Java anterior ):

#include <stdio.h>int leer ( int * p ) { devolver * p ; }void assign ( int * p , int v ) { * p = v ; }void swap ( int * a , int * b ) { int temp_storage ; int * temp = & temp_storage ; assign ( temp , read ( a )); assign ( a , read ( b )); assign ( b , read ( temp )); }int main () { int a_storage ; int * a = & a_storage ; int b_storage ; int * b = & b_storage ; assign ( a , 1 ); assign ( b , 2 ); swap ( a , b ); printf ( "%d %d \n " , read ( a ), read ( b )); // 2 1 return 0 ; }

Debido a que en este programa swapse opera con punteros y no se pueden cambiar los punteros en sí, sino solo los valores a los que apuntan, esta visión sostiene que la estrategia de evaluación principal de C es más similar a la de paso por compartición.

C++ complica aún más el asunto al permitir swapque se declare y utilice con una sintaxis de "referencia" muy ligera: [ 53 ]

void swap ( int & a , int & b ) { int temp = a ; a = b ; b = temp ; }int main () { int a = 1 ; int b = 2 ; swap ( a , b ); std :: println ( "{} {}" , a , b ); // 2 1 return 0 ; }

Semánticamente, esto es equivalente a los ejemplos de C. Por ello, muchos autores consideran que el paso de parámetros por dirección es una estrategia única, distinta del paso por valor, por referencia y por compartición.

Llamamiento a la unificación

En programación lógica , la evaluación de una expresión puede corresponder simplemente a la unificación de los términos involucrados, combinada con la aplicación de algún tipo de resolución . La unificación debe clasificarse como una estrategia de enlace estricto, ya que se realiza completamente. Sin embargo, la unificación también puede realizarse sobre variables no acotadas, por lo que las llamadas no necesariamente garantizan valores finales para todas sus variables.

Estrategias de vinculación no estrictas

Llamar por nombre

La llamada por nombre es una estrategia de evaluación en la que los argumentos de una función no se evalúan antes de que se llame a la función; en su lugar, se sustituyen directamente en el cuerpo de la función (mediante sustitución que evita la captura ) y se dejan para que se evalúen cada vez que aparecen en la función. Si un argumento no se utiliza en el cuerpo de la función, nunca se evalúa; si se utiliza varias veces, se reevalúa cada vez que aparece. (Véase el dispositivo de Jensen para una técnica de programación que aprovecha este principio).

En ocasiones, la evaluación por nombre es preferible a la evaluación por valor. Si el argumento de una función no se utiliza dentro de la misma, la evaluación por nombre ahorra tiempo al no evaluarlo, mientras que la evaluación por valor lo evalúa de todos modos. Si el argumento es un cálculo que no termina, la ventaja es enorme. Sin embargo, cuando se utiliza el argumento de la función, la evaluación por nombre suele ser más lenta, requiriendo un mecanismo como un thunk .

Los lenguajes .NET pueden simular la llamada por nombre mediante delegados o Expression<T>parámetros. Esto último da como resultado que se le pase un árbol de sintaxis abstracta a la función. Eiffel proporciona agentes, que representan una operación que se evaluará cuando sea necesario. Los programas Java pueden lograr una evaluación diferida similar mediante expresiones lambda y la java.util.function.Supplier<T>interfaz.

Llamar según sea necesario

La llamada por necesidad es una variante memorizada de la llamada por nombre, donde, si se evalúa el argumento de la función, ese valor se almacena para su uso posterior. Si el argumento es puro (es decir, libre de efectos secundarios), esto produce los mismos resultados que la llamada por nombre, ahorrando el costo de volver a calcular el argumento.

Haskell es un lenguaje muy conocido que utiliza evaluación por necesidad. Dado que la evaluación de expresiones puede ocurrir arbitrariamente lejos en un cálculo, Haskell solo admite efectos secundarios (como la mutación ) mediante el uso de mónadas . Esto elimina cualquier comportamiento inesperado de las variables cuyos valores cambian antes de su evaluación diferida.

En la implementación de R del método `call by need`, se pasan todos los argumentos, lo que significa que R permite efectos secundarios arbitrarios.

La evaluación perezosa es la implementación más común de la semántica de llamada por necesidad, pero existen variaciones como la evaluación optimista . Los lenguajes .NET implementan la llamada por necesidad utilizando el tipo Lazy<T>.

La reducción de grafos es una implementación eficiente de la evaluación perezosa.

Llamada por expansión macro

La llamada mediante expansión de macro es similar a la llamada por nombre, pero utiliza sustitución textual en lugar de sustitución que evita la captura . Por lo tanto, la sustitución de macro puede provocar la captura de variables, lo que conlleva errores y comportamientos no deseados. Las macros higiénicas evitan este problema al comprobar y reemplazar las variables ocultas que no son parámetros.

Llamada del futuro

La "llamada por futuro", también conocida como "llamada paralela por nombre" o "evaluación flexible" [ 54 ] , es una estrategia de evaluación concurrente que combina semántica no estricta con evaluación estricta. El método requiere una planificación y sincronización dinámicas de grano fino, pero es adecuado para máquinas masivamente paralelas.

La estrategia crea un futuro (promesa) para el cuerpo de la función y cada uno de sus argumentos. Estos futuros se calculan simultáneamente con el flujo del resto del programa. Cuando un futuro A requiere el valor de otro futuro B que aún no se ha calculado, el futuro A se bloquea hasta que el futuro B termine de calcularse y tenga un valor. Si el futuro B ya ha terminado de calcularse, el valor se devuelve inmediatamente. Las condicionales se bloquean hasta que se evalúa su condición, y las expresiones lambda no crean futuros hasta que se aplican por completo. [ 55 ]

Si se implementa con procesos o subprocesos, la creación de un futuro generará uno o más procesos o subprocesos nuevos (para las promesas). El acceso al valor los sincronizará con el subproceso principal, y la finalización del cálculo del futuro equivale a cancelar las promesas que calculan su valor. Si se implementa con una corrutina , como en .NET async/await , la creación de un futuro llama a una corrutina (una función asíncrona), que puede ceder el control al llamador y, a su vez, devolver el control cuando se utiliza el valor, lo que permite la multitarea cooperativa.

La estrategia no es determinista, ya que la evaluación puede ocurrir en cualquier momento entre la creación del futuro (es decir, cuando se da la expresión) y el uso del valor del futuro. La estrategia no es estricta porque el cuerpo de la función puede devolver un valor antes de que se evalúen los argumentos. Sin embargo, en la mayoría de las implementaciones, la ejecución aún puede quedarse atascada evaluando un argumento innecesario. Por ejemplo, el programa

f x = 1 / x g y = 1 principal = imprimir ( g ( f 0 ))

puede gterminar antes fy generar 1, o puede resultar en un error debido a la evaluación 1/0. [ 31 ]

La llamada por futuro es similar a la llamada por necesidad en que los valores se calculan solo una vez. Con un manejo cuidadoso de errores y no terminación, en particular terminando los futuros a mitad de camino si se determina que no serán necesarios, la llamada por futuro también tiene las mismas propiedades de terminación que la evaluación de llamada por necesidad. [ 55 ] Sin embargo, la llamada por futuro puede realizar trabajo especulativo innecesario en comparación con la llamada por necesidad, como evaluar profundamente una estructura de datos perezosa. [ 31 ] Esto se puede evitar usando futuros perezosos que no comienzan el cálculo hasta que sea seguro que el valor es necesario.

Evaluación optimista

La evaluación optimista es una variante de evaluación por necesidad en la que el argumento de la función se evalúa parcialmente mediante evaluación por valor durante un tiempo determinado (que puede ajustarse en tiempo de ejecución ). Transcurrido ese tiempo, la evaluación se interrumpe y la función se aplica mediante evaluación por necesidad. [ 56 ] Este enfoque evita algunos de los costes de ejecución de la evaluación por necesidad, al tiempo que conserva las características de terminación deseadas.

Véase también

Referencias

  1. Araki, Shota; Nishizaki, Shin-ya (noviembre de 2014). "Evaluación por nombre de los cálculos RPC y RMI". Teoría y práctica de la computación . pág.  1. doi : 10.1142/9789814612883_0001 . ISBN 978-981-4612-87-6Consultado el 21 de agosto de 2021 .
  2. Turbak, Franklyn; Gifford, David (18 de julio de 2008). Conceptos de diseño en lenguajes de programación . MIT Press. pág. 309. ISBN  978-0-262-30315-6.
  3. Crank, Erik; Felleisen, Matthias (1991). "Paso de parámetros y cálculo lambda". Actas del 18.º simposio ACM SIGPLAN-SIGACT sobre principios de lenguajes de programación - POPL '91 . p. 2. CiteSeerX 10.1.1.23.4385 . doi : 10.1145/99583.99616 . ISBN   0897914198. S2CID 5782416 . 
  4. 1 2 3 Guillermo, Reinhard; Seidl, Helmut (10 de noviembre de 2010). Diseño de compiladores: máquinas virtuales . Medios de ciencia y negocios de Springer. pag. 61.ISBN  978-3-642-14909-2.
  5. Nita, Stefanía Loredana; Mihailescu, Marius (2017). "Introducción" . Haskell concurrente práctico . pag. 3.doi : 10.1007 /978-1-4842-2781-7_1 . ISBN  978-1-4842-2780-0.
  6. Pierce, Benjamin C. (2002). Tipos y lenguajes de programación . MIT Press . pág. 56. ISBN  0-262-16209-1.
  7. "Evaluar (v.), sentido a". Oxford English Dictionary . 2023. doi : 10.1093/OED/3423541985 . Matemáticas. Calcular el 'valor' de (una expresión cuantitativa); encontrar una expresión numérica para (cualquier hecho o relación cuantitativa).
  8. "Simplificar (v.), sentido 4.a". Oxford English Dictionary . 2023. doi : 10.1093/OED/1018661347 . Expresar (una ecuación u otra expresión matemática) de una forma más fácil de entender, analizar o usar, por ejemplo, agrupando términos semejantes o sustituyendo variables.
  9. Daniel P. Friedman; Mitchell Wand (2008). Fundamentos de los lenguajes de programación (tercera ed.). Cambridge, MA: The MIT Press . ISBN  978-0262062794.
  10. 1 2 Scott, Michael Lee (2016). Pragmática del lenguaje de programación (Cuarta ed.). Waltham, MA: Elsevier. ISBN  9780124104778.
  11. Kernighan, Brian W.; Ritchie, Dennis M. (1988). El lenguaje de programación C (2.ª ed.). Englewood Cliffs, NJ: Prentice Hall. pág. 28. ISBN   978-0131103627.
  12. "Evite copias innecesarias de datos - MATLAB y Simulink" . www.mathworks.com . Consultado el 28 de enero de 2023 .
  13. Hasti, Rebecca. "Paso de parámetros" . CS 536: Introducción a los lenguajes de programación y compiladores . Universidad de Wisconsin . Consultado el 22 de agosto de 2021 .
  14. JA Robinson (enero de 1965). "Una lógica orientada a máquinas basada en el principio de resolución" . Journal of the ACM . 12 (1): 23– 41. doi : 10.1145/321250.321253 . S2CID 14389185 . Aquí: sección 5.8, pág. 32
  15. JA Robinson (1971). "Lógica computacional: La computación de unificación" . Inteligencia de máquinas . 6 : 63–72 .
  16. Bundy, Alan; Wallen, Lincoln (1984). "SASL". Catálogo de herramientas de inteligencia artificial . pág. 117. doi : 10.1007/978-3-642-96868-6_222 . ISBN  978-3-540-13938-6Probablemente fue el primer lenguaje en explotar sistemáticamente el poder de la evaluación perezosa.
  17. Fay, Colin (30 de julio de 2018). "Sobre la evaluación perezosa" . R-bloggers . Recuperado el 21 de agosto de 2021 .
  18. Wadsworth, Christopher P. (1971). Semántica y pragmática del cálculo lambda (tesis doctoral). Universidad de Oxford.
  19. 1 2 3 4 5 Liskov, Barbara; Atkinson, Russ; Bloom, Toby; Moss, Eliot; Schaffert, Craig; Scheifler, Craig; Snyder, Alan (octubre de 1979). "Manual de referencia de CLU" (PDF) . Laboratorio de Ciencias de la Computación . Instituto Tecnológico de Massachusetts. págs. 14–15 . Archivado (PDF) del original el 22 de septiembre de 2006. Recuperado el 19 de mayo de 2011 . 
  20. "PHP: Passing by Reference - Manual" . www.php.net . Consultado el 4 de julio de 2021 .
  21. Wagner, Bill (12 de abril de 2023). "Paso de parámetros: guía de programación en C#" . Microsoft Docs . Consultado el 10 de septiembre de 2023 .
  22. Dollard, Kathleen (15 de septiembre de 2021). "Paso de argumentos por valor y por referencia - Visual Basic" . Microsoft Docs . Consultado el 10 de septiembre de 2023 .
  23. 1 2 "Historia de C++" . en.cppreference.com . Consultado el 11 de junio de 2022 .
  24. Filipek, Bartlomiej (16 de agosto de 2021). "Orden de evaluación de expresiones más estricto en C++17" . C++ Stories . Consultado el 24 de agosto de 2021 .
  25. 1 2 Abelson, Harold ; Sussman, Gerald Jay (1996). "Orden normal y orden aplicativo" . Estructura e interpretación de programas informáticos (2.ª ed.). Cambridge, Massachusetts: MIT Press . ISBN  0-262-01153-0Archivado del original el 2 de marzo de 2005. Consultado el 6 de marzo de 2006 .Véase también la nota al pie Temp 576.
  26. Reese, Richard M. (14 de octubre de 2015). Aprendiendo programación funcional en Java . Packt Publishing Ltd. pág. 106. ISBN  978-1-78528-935-4.
  27. Antani, Ved; Timms, Simon; Mantyla, Dan (31 de agosto de 2016). JavaScript: Programación funcional para desarrolladores de JavaScript . Packt Publishing Ltd. pág. 614. ISBN  978-1-78712-557-5.
  28. Seacord, Robert C. "EXP30-C. No dependa del orden de evaluación para los efectos secundarios" . Estándar de codificación SEI CERT C. Universidad Carnegie Mellon . Consultado el 23 de agosto de 2021 .
  29. Anglade, S.; Lacrampe, JJ; Queinnec, C. (octubre de 1994). "Semántica de combinaciones en Scheme" (PDF) . ACM SIGPLAN Lisp Pointers . VII (4): 15–20 . doi : 10.1145/382109.382669 . S2CID 2987427 . 
  30. "¿Por qué los argumentos de las funciones de OCaml se evalúan de derecha a izquierda?" . OCaml . 30 de noviembre de 2017.
  31. 1 2 3 4 5 Tremblay, G. (abril de 2000). "La evaluación indulgente no es ni estricta ni perezosa". Computer Languages . 26 (1): 43– 66. CiteSeerX 10.1.1.137.9885 . doi : 10.1016/S0096-0551(01)00006-6 . 
  32. George, Lai (marzo de 1987). Evaluación eficiente del orden normal a través de información de estrictez (MSc). Universidad de Utah. pág. 10. 
  33. Borning, Alan (otoño de 1999). "Evaluación de orden aplicativo frente a orden normal en lenguajes funcionales" (PDF) . CSE 505: Conceptos de lenguajes de programación . Universidad de Washington . Consultado el 23 de agosto de 2021 .
  34. Mazzola, Guerino; Milmeister, Gérard; Weissmann, Jody (21 de octubre de 2004). Matemáticas integrales para informáticos 2. Springer Science & Business Media. pág. 323. ISBN  978-3-540-20861-7.
  35. 1 2 Sturm, Oliver (11 de abril de 2011). Programación funcional en C#: Técnicas de programación clásicas para proyectos modernos . John Wiley and Sons. pág. 91. ISBN  978-0-470-74458-1.
  36. Marlow, Simon. "¿Por qué no puedo obtener un rastreo de pila?" . Haskell Implementors Workshop 2012 . Consultado el 25 de agosto de 2021 .
  37. Nilsson, Henrik (1999). "Rastreo pieza por pieza: Depuración asequible para lenguajes funcionales perezosos". Actas de la cuarta conferencia internacional ACM SIGPLAN sobre programación funcional . págs. 36–47 . CiteSeerX 10.1.1.451.6513 . doi : 10.1145/317636.317782 . ISBN   1581131119. S2CID 13954359 . 
  38. "Abrir parámetros de matriz" . www.freepascal.org . Consultado el 20 de enero de 2024 .
  39. 1 2 3 4 "¡Java es paso por valor, maldita sea!" . 16 de mayo de 2001 . Consultado el 24 de diciembre de 2016 .
  40. ^ Coenen, Frans. "PASO DE PARÁMETROS" . cgi.csc.liv.ac.uk. ​Consultado el 22 de enero de 2024 .
  41. "Llamada por referencia, problemas de alias" (PDF) . Curso MPRI 2-36-1: Prueba de programa (apuntes de clase) . pág. 53. Archivado del original (PDF) el 25 de junio de 2024. 
  42. Manual de referencia del idioma Ada 2022 (PDF) . 13 de octubre de 2023. pág. 215. 
  43. Barnes, John (2013). Fundamentos de Ada 2012: el lenguaje, las bibliotecas estándar (PDF) . Heidelberg: Springer. pp. 15–16 , 87–88 . ISBN  978-3-642-45210-9.
  44. Thurlow, Robert (mayo de 2009). "RPC: Especificación del protocolo de llamada a procedimiento remoto, versión 2" . tools.ietf.org . IETF . Consultado el 7 de abril de 2018 .
  45. Lundh, Fredrik. "Llamada por objeto" . Effbot.org . Archivado del original el 19 de mayo de 2011. Consultado el 19 de mayo de 2011 .
  46. Jones, Rhys Price (2010). "¿Es Scheme un método de paso por valor?" . CS 145 Lenguajes de Programación, Laboratorio 9: Paso de Parámetros . Universidad George Washington. Archivado del original el 16 de octubre de 2014. Recuperado el 20 de enero de 2024 .
  47. " Procedimientos de la biblioteca Tcl - Página del manual Tcl_Obj" . www.tcl.tk.
  48. "CA1021: Evitar parámetros de salida" . Microsoft. 15 de noviembre de 2016.
  49. Leo, Ray (noviembre de 1996). Little C++ (Made Easy) . LeoSudo Inc. págs. 79–80 . ISBN  978-0-9654634-1-6.
  50. Dandamudi, Sivarama P. (15 de julio de 2005). Guía de programación en lenguaje ensamblador en Linux . Springer Science & Business Media. pág. 232. ISBN  978-0-387-25897-3.
  51. Srivastava, SK; Srivastava, Deepali (6 de junio de 2018). C en profundidad . BPB Publications. pág. 206. ISBN  978-93-87284-94-4.
  52. "Variables mutables y tipos de referencia" . okmij.org . Consultado el 20 de enero de 2024 .
  53. Vermeir, Dirk (28 de junio de 2011). Programación multiparadigma con C++ . Springer Science & Business Media. págs. 10–11 . ISBN  978-1-4471-0311-0.
  54. McCollin, Thomas Gwynfryn; Morell, Tobias. "Un juego de paradigmas: un estudio de usabilidad de modismos funcionales en la programación de juegos" (PDF) . Universidad de Aalborg. pág. 6. Consultado el 11 de enero de 2022 . 
  55. 1 2 Schauser, Klaus E.; Goldstein, Seth C. (1995). "¿Cuánta flexibilidad requieren los programas permisivos?" (PDF) . Actas de la séptima conferencia internacional sobre lenguajes de programación funcional y arquitectura de computadoras - FPCA '95 . págs. 216–225 . doi : 10.1145/224164.224208 . ISBN  0897917197. S2CID 2045943 . Consultado el 7 de enero de 2022 . 
  56. Ennals, Robert; Peyton Jones, Simon (agosto de 2003). Optimistic Evaluation: a fast evaluation strategy for non-strict programs . Conferencia Internacional sobre Programación Funcional. ACM Press. Archivado del original el 22 de agosto de 2025.

Lecturas adicionales

  • Baker-Finch, Clem; King, David; Hall, Jon; Trinder, Phil (10 de marzo de 1999). "Una semántica operacional para la llamada paralela por necesidad" (ps) . Informe de investigación . 99 (1). Facultad de Matemáticas e Informática, The Open University.
  • Ennals, Robert; Peyton Jones, Simon (agosto de 2003). Evaluación optimista: una estrategia de evaluación rápida para programas no estrictos (PDF) . Conferencia Internacional sobre Programación Funcional. ACM Press. Archivado del original (PDF) el 22 de agosto de 2025.
  • Ludäscher, Bertram (24-01-2001). "Apuntes de clase de CSE 130" . CSE 130: Lenguajes de programación: principios y paradigmas .
  • Pierce, Benjamin C. (2002). Tipos y lenguajes de programación . MIT Press . ISBN 0-262-16209-1.
  • Sestoft, Peter (2002). «Demostración de la reducción del cálculo lambda» (PDF) . En Mogensen, T; Schmidt, D; Sudborough, IH (eds.). La esencia de la computación: complejidad, análisis, transformación. Ensayos dedicados a Neil D. Jones . Lecture Notes in Computer Science. Vol.  2566. Springer-Verlag. pp. 420–435 . ISBN  3-540-00326-6.
  • "Paso por valor y paso por referencia en programación C" . Explicación del paso por valor y paso por referencia en programación C.{{cite web}}: CS1 maint: servicio de archivado obsoleto ( enlace )
  • El visualizador interactivo en línea Geometría de la Interacción implementa un sistema basado en grafos para diversas estrategias de evaluación comunes.