En la teoría de lenguajes de programación , la evaluación perezosa , o llamada por necesidad , [ 1 ] es una estrategia de evaluación que retrasa la evaluación de una expresión hasta que se necesita su valor ( evaluación no estricta ) y que evita evaluaciones repetidas (mediante el uso de compartición ). [ 2 ] [ 3 ]
Entre los beneficios de la evaluación perezosa se incluyen:
- La capacidad de definir el flujo de control (estructuras) como abstracciones en lugar de primitivas .
- La capacidad de definir estructuras de datos potencialmente infinitas . Esto permite una implementación más sencilla de algunos algoritmos .
- La capacidad de definir estructuras de datos parcialmente definidas donde algunos elementos son errores. Esto permite la creación rápida de prototipos .
La evaluación perezosa se combina a menudo con la memorización , como se describe en el libro Writing Efficient Programs de Jon Bentley . [ 4 ] Después de calcular el valor de una función para ese parámetro o conjunto de parámetros, el resultado se almacena en una tabla de búsqueda indexada por los valores de dichos parámetros; la próxima vez que se llame a la función, se consulta la tabla para determinar si el resultado para esa combinación de valores de parámetros ya está disponible. Si es así, simplemente se devuelve el resultado almacenado. Si no, se evalúa la función y se agrega otra entrada a la tabla de búsqueda para su reutilización.
La evaluación perezosa es difícil de combinar con características imperativas como el manejo de excepciones y la entrada/salida , porque el orden de las operaciones se vuelve indeterminado.
Lo opuesto a la evaluación perezosa es la evaluación estricta , también conocida como evaluación rigurosa. La evaluación estricta es la estrategia de evaluación empleada en la mayoría de los lenguajes de programación .
Historia
La evaluación perezosa fue introducida para el cálculo lambda por Christopher Wadsworth. [ 5 ] Para los lenguajes de programación, fue introducida independientemente por Peter Henderson y James H. Morris [ 6 ] y por Daniel P. Friedman y David S. Wise. [ 7 ] [ 8 ]
Aplicaciones
La evaluación diferida se utiliza particularmente en lenguajes de programación funcional . Al usar la evaluación diferida, una expresión no se evalúa tan pronto como se asigna a una variable, sino cuando el evaluador se ve obligado a producir el valor de la expresión. Es decir, una instrucción como x = expression;(es decir, la asignación del resultado de una expresión a una variable) claramente requiere que la expresión se evalúe y el resultado se coloca en x, pero lo que realmente hay en xes irrelevante hasta que se necesite su valor a través de una referencia a xen alguna expresión posterior cuya evaluación podría diferirse, aunque eventualmente el árbol de dependencias de rápido crecimiento se podaría para producir algún símbolo en lugar de otro para que el mundo exterior lo vea. [ 9 ]
La evaluación perezosa es fundamental en marcos de big data como Apache Spark , donde los cálculos en conjuntos de datos distribuidos se retrasan hasta que los resultados se necesitan explícitamente, lo que permite optimizaciones de ejecución y reducción de procesamiento innecesario. [ 10 ]
Estructuras de control
La evaluación perezosa permite definir estructuras de control de forma normal, y no como primitivas o técnicas de tiempo de compilación. Por ejemplo, se pueden definir operadores de evaluación if-then-else y de cortocircuito : [ 11 ] [ 12 ]
Si entonces es verdadero b c = b Si entonces es falso b c = c-- o Verdadero || b = Verdadero Falso || b = b--y Verdadero && b = b Falso && b = FalsoEstas tienen la semántica habitual, es decir, evalúa (a), luego si y solo si (a) se evalúa como verdadero evalúa (b), de lo contrario evalúa (c). Es decir, exactamente uno de (b) o (c) será evaluado. De manera similar, para , si la parte fácil da verdadero, la expresión de mucho trabajo podría evitarse. Finalmente, al evaluar , si SafeToTry es falso, no habrá ningún intento de evaluar la expresión .ifThenElseabcEasilyComputed||LotsOfWorkSafeToTry&&Expression
Por el contrario, en un lenguaje de evaluación estricta, la definición anterior evaluaría (a), (b) y (c) independientemente del valor de (a). Este no es el comportamiento deseado, ya que (b) o (c) pueden tener efectos secundarios , tardar mucho tiempo en calcularse o generar errores. Por lo general, es posible introducir estructuras de control perezosas definidas por el usuario en lenguajes de evaluación estricta como funciones, aunque pueden apartarse de la sintaxis del lenguaje para la evaluación estricta: a menudo, los cuerpos de código involucrados deben estar envueltos en un valor de función, de modo que se ejecuten solo cuando se les llame.ifThenElseabc
Trabajar con estructuras de datos infinitas
La evaluación diferida tiene la ventaja de poder crear listas infinitas calculables sin bucles infinitos ni problemas de tamaño que interfieran en el cálculo. Los valores reales solo se calculan cuando son necesarios. Por ejemplo, se podría crear una función que cree una lista infinita (a menudo llamada flujo ) de números de Fibonacci . El cálculo del n -ésimo número de Fibonacci consistiría simplemente en extraer ese elemento de la lista infinita, lo que obliga a evaluar solo los primeros n miembros de la lista. [ 13 ] [ 14 ]
Tomemos como ejemplo este programa trivial en Haskell :
numberFromInfiniteList :: Int -> Int numberFromInfiniteList n = infinity !! n - 1 donde infinity = [ 1 .. ]principal = imprimir $ númeroDeListaInfinita 4En la función numberFromInfiniteList , el valor de infinito es un rango infinito, pero hasta que se necesite un valor real (o más específicamente, un valor específico en un índice determinado), la lista no se evalúa, e incluso entonces, solo se evalúa según sea necesario (es decir, solo hasta el índice deseado). Si el programador tiene cuidado, el programa se completa normalmente. Sin embargo, ciertos cálculos pueden provocar que el programa intente evaluar un número infinito de elementos; por ejemplo, solicitar la longitud de la lista o intentar sumar los elementos de la lista con una operación de plegado provocaría que el programa no terminara o se quedara sin memoria .
Como otro ejemplo, la lista de todos los números de Fibonacci se puede escribir en el lenguaje de programación Haskell como: [ 14 ]
fibs = 0 : 1 : zipWith ( + ) fibs ( tail fibs )En la sintaxis de Haskell, " :" antepone un elemento a una lista, taildevuelve una lista sin su primer elemento y zipWithutiliza una función específica (en este caso, la suma) para combinar los elementos correspondientes de dos listas para producir una tercera. [ 13 ]
Patrón de lista de éxitos
Otros usos
En los sistemas de ventanas de computadora , la representación de la información en la pantalla está controlada por eventos de exposición que controlan el código de visualización en el último momento posible. Al hacer esto, los sistemas de ventanas evitan calcular actualizaciones innecesarias del contenido de la pantalla. [ 15 ]
Otro ejemplo de pereza en los sistemas informáticos modernos es la asignación de páginas por copia en escritura o paginación bajo demanda , donde la memoria se asigna solo cuando se modifica un valor almacenado en ella. [ 15 ]
La carga diferida puede ser útil en escenarios de alto rendimiento. Un ejemplo es la función mmap de Unix , que permite la carga de páginas desde el disco bajo demanda , de modo que solo se cargan en memoria las páginas que realmente se utilizan y no se asigna memoria innecesaria.
MATLAB implementa la copia en edición , donde los arreglos que se copian tienen su almacenamiento de memoria real replicado solo cuando se cambia su contenido, lo que puede provocar un error de falta de memoria al actualizar un elemento posteriormente en lugar de durante la operación de copia. [ 16 ]
Actuación
El número de reducciones beta para reducir un término lambda con llamada por necesidad no es mayor que el número necesario con la llamada por valor o la llamada por nombre . [ 17 ] [ 18 ] Con ciertos programas, el número de pasos puede ser mucho menor; por ejemplo, una familia específica de términos lambda que utiliza numerales de Church requiere una cantidad infinita de pasos con llamada por valor (es decir, nunca se completa), un número exponencial de pasos con llamada por nombre, pero solo un número polinomial con llamada por necesidad. La llamada por necesidad incorpora dos optimizaciones: nunca repetir el trabajo (similar a la llamada por valor) y nunca realizar trabajo innecesario (similar a la llamada por nombre). [ 19 ] La evaluación perezosa también puede conducir a una reducción en la huella de memoria , ya que los valores se crean cuando se necesitan. [ 20 ]
En la práctica, la evaluación perezosa puede causar problemas de rendimiento significativos en comparación con la evaluación estricta. Por ejemplo, en las arquitecturas informáticas modernas, retrasar un cálculo y realizarlo posteriormente es más lento que realizarlo inmediatamente . Esto puede mitigarse mediante el análisis de estrictez . [ 19 ] La evaluación perezosa también puede introducir fugas de memoria debido a expresiones no evaluadas. [ 21 ] [ 22 ]
Implementación
Algunos lenguajes de programación retrasan la evaluación de expresiones por defecto, y otros proporcionan funciones o sintaxis especial para retrasarla. En KRC , Miranda y Haskell , la evaluación de los argumentos de las funciones se retrasa por defecto. En muchos otros lenguajes, la evaluación se puede retrasar suspendiendo explícitamente el cálculo mediante sintaxis especial (como con " " y " " de Scheme y " " y " " de OCaml ) o, de forma más general, envolviendo la expresión en un thunk . El objeto que representa dicha evaluación explícitamente retrasada se denomina futuro perezoso . Raku utiliza la evaluación perezosa de listas, por lo que se pueden asignar listas infinitas a variables y utilizarlas como argumentos de funciones, pero a diferencia de Haskell y Miranda, Raku no utiliza la evaluación perezosa de operadores aritméticos y funciones por defecto. [ 9 ]delayforcelazyLazy.force
Pereza y entusiasmo
Controlar el entusiasmo en los lenguajes perezosos
En lenguajes de programación perezosos como Haskell, aunque por defecto se evalúan las expresiones solo cuando se requieren, en algunos casos es posible hacer que el código sea más exigente (o, a la inversa, volverlo más perezoso después de haberlo hecho más exigente). Esto se puede lograr codificando explícitamente algo que fuerce la evaluación (lo que puede hacer que el código sea más exigente) o evitando dicho código (lo que puede hacer que el código sea más perezoso). La evaluación estricta generalmente implica exigencia, pero técnicamente son conceptos diferentes.
Sin embargo, algunos compiladores implementan una optimización llamada análisis de estrictez , que, en ciertos casos, permite al compilador inferir que un valor siempre se utilizará. En tales casos, esto puede hacer que la decisión del programador de forzar o no ese valor en particular resulte irrelevante, ya que el análisis de estrictez forzará una evaluación estricta .
En Haskell, marcar los campos del constructor como estrictos significa que sus valores siempre se exigirán de inmediato. La seqfunción también puede usarse para exigir un valor inmediatamente y luego pasarlo, lo cual es útil si un campo del constructor debe ser generalmente perezoso. Sin embargo, ninguna de estas técnicas implementa la estrictez recursivadeepSeq ; para eso, se inventó una función llamada .
Además, la coincidencia de patrones en Haskell 98 es estricta por defecto, por lo que ~se debe usar el calificador para hacerla perezosa. [ 23 ]
Simulación de la pereza en lenguajes entusiastas
C++
En C++ , la std::rangesbiblioteca utiliza adaptadores de rango perezosos. [ 24 ]
importar std ;using std :: vector ; using std :: ranges :: to ; using std :: views :: filter ; using std :: views :: iota ; using std :: views :: transform ;vector < int > v = iota ( 1 , 1'000'000 ) // genera enteros de forma perezosa desde 1 hasta 1,000,000 | filter ([]( int x ) -> bool { return x % 2 == 0 ; }) // filtra los números impares, conservando los pares | transform ([]( int x ) -> int { return x * x ; }) // eleva al cuadrado cada valor | to < vector > (); // convierte el rango de nuevo a un vector<int>Java
En Java , la evaluación perezosa se puede realizar utilizando objetos que tengan un método para evaluarlos cuando se necesite su valor. El cuerpo de este método debe contener el código necesario para realizar dicha evaluación. Desde la introducción de las expresiones lambda en Java SE8, Java ha admitido una notación compacta para esto. La siguiente interfaz genérica de ejemplo proporciona un marco para la evaluación perezosa: [ 25 ] [ 26 ]
interfaz Lazy < T > { T eval (); }La Lazyinterfaz con su eval()método es equivalente a la Supplierinterfaz con su get()método en la java.util.functionbiblioteca. [ 27 ] [ 28 ] : 200
Cada clase que implementa la Lazyinterfaz debe proporcionar un evalmétodo, y las instancias de la clase pueden contener los valores que el método necesite para realizar la evaluación perezosa. Por ejemplo, considere el siguiente código para calcular e imprimir de forma perezosa 2 10 :
Lazy < Integer > a = () -> 1 ; for ( int i = 0 ; i < 10 ; i ++ ) { Lazy < Integer > b = a ; a = () -> b . eval () + b . eval (); } System . out . println ( "a = " + a . eval ());En lo anterior, la variable a se refiere inicialmente a un objeto entero perezoso creado por la expresión lambda . Evaluar esta expresión lambda es similar a construir una nueva instancia de una clase anónima que implementa con un método eval que devuelve 1 .() -> 1Lazy<Integer>
Cada iteración del bucle vincula `a` con un nuevo objeto creado al evaluar la expresión lambda dentro del bucle. Cada uno de estos objetos contiene una referencia a otro objeto de evaluación diferida, `b` , y tiene un método `eval` que se llama dos veces y devuelve la suma. La variable `b` es necesaria aquí para cumplir con el requisito de Java de que las variables referenciadas dentro de una expresión lambda sean efectivamente finales.b.eval()
Este programa es ineficiente porque esta implementación de enteros perezosos no almacena en memoria el resultado de las llamadas previas a `eval` . También implica un considerable uso de `autoboxing` y `unboxing` . Lo que puede no ser obvio es que, al final del bucle, el programa ha construido una lista enlazada de 11 objetos y que todas las sumas necesarias para calcular el resultado se realizan en respuesta a la llamada a la función en la última línea de código. Esta llamada recorre recursivamente la lista para realizar las sumas necesarias.a.eval()
Podemos construir una clase Java que memorizara un objeto perezoso de la siguiente manera: [ 25 ] [ 26 ]
clase Memo < T > implementa Lazy < T > { private Lazy < T > lazy ; // una expresión perezosa, eval la establece en null private T memo ; // el memorándum del valor anteriorpublic Memo ( Lazy < T > lazy ) { this . lazy = lazy ; }public T eval () { if ( lazy != null ) { memo = lazy.eval ( ) ; lazy = null ; } return memo ; } }Esto permite reescribir el ejemplo anterior para que sea mucho más eficiente. Mientras que el original se ejecutaba en un tiempo exponencial con respecto al número de iteraciones, la versión memorizada se ejecuta en un tiempo lineal :
Lazy < Integer > a = () -> 1 ; for ( int i = 0 ; i < 10 ; i ++ ) { Lazy < Integer > b = a ; a = new Memo < Integer > (() -> b . eval () + b . eval ()); } System . out . printf ( "a = %s%n" , a . eval ());Las expresiones lambda de Java son simplemente azúcar sintáctico . Todo lo que se puede escribir con una expresión lambda se puede reescribir como una llamada para construir una instancia de una clase interna anónima que implementa la interfaz, [ a ] y cualquier uso de una clase interna anónima se puede reescribir usando una clase interna con nombre, y cualquier clase interna con nombre se puede mover al nivel de anidamiento más externo.
JavaScript
En JavaScript , la evaluación perezosa se puede simular utilizando un generador . Por ejemplo, la secuencia de todos los números de Fibonacci se puede escribir, utilizando memorización , en el siguiente código TypeScript :
/** * Las funciones generadoras devuelven objetos generadores, que reifican la evaluación perezosa. * @return {!Generator<bigint>} Un generador de enteros no nulo. */ function * fibonacciNumbers () : Generator < bigint , never , unknown > { let memo : [ bigint , bigint ] = [ 1n , - 1n ]; // crea el estado inicial (por ejemplo, un vector de números "negafibonacci") while ( true ) { // repite indefinidamente memo = [ memo [ 0 ] + memo [ 1 ], memo [ 0 ]]; // actualiza el estado en cada evaluación yield memo [ 0 ]; // devuelve el siguiente valor y suspende la ejecución hasta que se reanude } }const stream : Generator < bigint , never , unknown > = fibonacciNumbers (); // crea un flujo de números evaluado de forma perezosa const first10: bigint[] = Array.from( new Array ( 10 ) , ( ) = > stream.next ( ) . value ) ; // evalúa solo los primeros 10 números console.log ( first10 ); // la salida es [0n , 1n, 1n, 2n, 3n, 5n , 8n, 13n, 21n, 34n]Kotlin
En Kotlin, las variables se pueden inicializar de forma diferida utilizando la función [ 29 ] para inicializar propiedades delegadas [ 30 ] con la cláusula de la siguiente manera:lazy{}by
var flipped = trueval pizzaReady : Boolean by lazy { println ( "Voy a comprobar si la pizza está lista ahora mismo." ) // Si esto se ejecuta más de una vez, mostrará false. flipped = ! flipped ! flipped }fun main () { println ( "¿Está lista mi pizza?" ) assert ( pizzaReady ) // no falla println ( "¿Sigue lista?" ) assert ( pizzaReady ) // sigue sin fallar }Puedes ejecutar este ejemplo tú mismo accediendo a Kotlin Playground .
Verificación
Para verificar que el programa se cargó de forma diferida isThePizzaReadyen lugar de inicializarse previamente como verdadero, necesitamos comprobar la salida del programa.
¿Está lista mi pizza? Voy a comprobar si la pizza está lista ahora mismo. ¿Sigue lista?Como podemos ver, cuando el programa lee, isThePizzaReadyimprime una instrucción que colocamos dentro del bloque de código perezoso en la línea 4. También podemos verificar que no ejecutó el bloque perezoso dos veces y reutilizó su salida anterior gracias a que:
- El programa no arrojó una excepción, la función assert arrojará una
AssertErrorsi la salida deisThePizzaReadyes falsa. [ 31 ] lo cual solo puede ocurrir si el bloque lazy se ejecuta más de una vez. - La instrucción de la línea 4 no se ejecuta más de una vez.
Pitón
En Python 2.x, la range()función [ 32 ] calcula una lista de enteros. La lista completa se almacena en la memoria cuando se evalúa la primera instrucción de asignación, por lo que este es un ejemplo de evaluación inmediata o inmediata:
r = rango ( 10 ) print r # imprime [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] print r [ 3 ] # imprime 3En Python 3.x, la range()función [ 33 ] devuelve un generador que calcula los elementos de la lista bajo demanda. Los elementos solo se generan cuando se necesitan (por ejemplo, cuando print(r[3])se evalúa en el siguiente ejemplo), por lo que este es un ejemplo de evaluación perezosa o diferida:
r : Iterador [ int ] = rango ( 10 ) print ( r ) # imprime rango(0, 10) print ( r [ 3 ]) # imprime 3- Este cambio a la evaluación perezosa ahorra tiempo de ejecución para rangos grandes que quizás nunca se referencien por completo y ahorra memoria para rangos grandes donde solo se necesitan uno o unos pocos elementos en un momento dado.
En Python 2.x es posible usar una función xrange()que devuelve un objeto que genera los números dentro del rango deseado. La ventaja es xrangeque el objeto generado siempre ocupará la misma cantidad de memoria.
r = xrange ( 10 ) print r # imprime xrange(10) lst = [ x for x in r ] print lst # imprime [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]A partir de la versión 2.2, Python manifiesta la evaluación perezosa mediante la implementación de iteradores (secuencias perezosas), a diferencia de las secuencias de tuplas o listas. Por ejemplo (Python 2):
números : Iterador [ int ] = rango ( 10 ) iterador : Iterador [ int ] = iter ( números ) imprimir ( números ) # imprime [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] imprimir ( iterador ) # imprime <objeto listiterador en 0xf7e8dd4c> imprimir ( iterador.next ( ) ) # imprime 0- El ejemplo anterior muestra que las listas se evalúan cuando se llaman, pero en el caso de un iterador , el primer elemento '0' se imprime cuando surge la necesidad.
.NETO
En el framework .NET , es posible realizar una evaluación diferida utilizando la clase . [ 34 ] La clase se puede aprovechar fácilmente en F# utilizando la palabra clave , mientras que el método forzará la evaluación. También existen colecciones especializadas como que proporcionan soporte integrado para la evaluación diferida.System.Lazy<T>lazyforceMicrosoft.FSharp.Collections.Seq
let fibonacci = Seq.unfold ( fun ( x , y ) - > Some ( x , ( y , x + y ) ) ) ( 0I , 1I ) fibonacci | > Seq.nth 1000En C# y VB.NET, la clase se utiliza directamente.System.Lazy<T>
usando el sistema ;public int Suma () { int a = 0 ; int b = 0 ; Lazy < int > x = new (() => a + b ); a = 3 ; b = 5 ; return x . Valor ; // devuelve 8 }O con un ejemplo más práctico:
usando el sistema ;// Cálculo recursivo del n-ésimo número de Fibonacci public int Fib ( int n ) { return ( n == 1 ) ? 1 : ( n == 2 ) ? 1 : Fib ( n - 1 ) + Fib ( n - 2 ); }public void Main () { Console.WriteLine ( " ¿Qué número de Fibonacci desea calcular?"); int n = Int32.Parse(Console.ReadLine ( ) ) ; Lazy <int> fib = new ( ( ) = > Fib ( n ) ) ; // La función está preparada , pero no se ejecuta bool execute ; if ( n > 100 ) { Console.WriteLine ( "Esto puede tardar un tiempo. ¿Realmente desea calcular este número tan grande? [y/n]" ) ; execute = ( Console.ReadLine () == "y " ) ; } else { execute = true ; }if ( execute ) { Console.WriteLine ( fib.Value ) ; // El número solo se calcula si es necesario } }Otra forma es utilizar la yieldpalabra clave:
usando System.Collections.Generic ;// evaluación inmediata public IEnumerable < int > Fibonacci ( int x ) { IList < int > fibs = new List < int > ();int prev = - 1 ; int next = 1 ; for ( int i = 0 ; i < x ; i ++ ) { int sum = prev + next ; prev = next ; next = sum ; fibs . Add ( sum ); } return fibs ; }// evaluación perezosa public IEnumerable < int > LazyFibonacci ( int x ) { int prev = - 1 ; int next = 1 ; for ( int i = 0 ; i < x ; i ++ ) { int sum = prev + next ; prev = next ; next = sum ; yield return sum ; } }Véase también
- Lógica combinatoria
- Zurra
- Flujo de datos
- Evaluación entusiasta
- Programación funcional
- Futuros y promesas
- Generador (programación informática)
- Reducción de gráficos
- Computación incremental : un concepto relacionado en el que los cálculos solo se repiten si cambian sus datos de entrada. Puede combinarse con la evaluación perezosa.
- Cálculo lambda
- Inicialización perezosa
- Mirar hacia adelante
- Lenguaje de programación no estricto
- evaluación de orden normal
- Evaluación de cortocircuito (mínima)
Notas
- 1 2 Las expresiones lambda de Java no son exactamente equivalentes a las clases anónimas; consulte Función anónima#Diferencias con respecto a las clases anónimas.
Referencias
- ↑ Hudak 1989 , pág. 384
- ↑ David Anthony Watt; William Findlay (2004). Conceptos de diseño de lenguajes de programación . John Wiley and Sons. págs. 367–368 . ISBN 978-0-470-85320-7. Consultado el 30 de diciembre de 2010 .
- ↑ Reynolds 1998 , pág. 307
- ↑ Bentley, Jon Louis. Escritura de programas eficientes. Prentice-Hall, 1985. ISBN 978-0139702440
- ↑ Wadsworth 1971
- ↑ Henderson y Morris 1976
- ↑ Friedman y Wise 1976
- ↑ Reynolds 1998 , pág. 312
- 1 2 Casas, A.; Cabeza, D.; Hermenegildo, MV (2006). "Un enfoque sintáctico para combinar la notación funcional, la evaluación perezosa y el orden superior en sistemas LP" . En Hagiya, M.; Wadler, P. (eds.). Programación funcional y lógica, FLOPS 2006. Lecture Notes in Computer Science. Vol. 3945. Springer. p. 149. doi : 10.1007/11737414_11 . ISBN 978-3-540-33438-5Consultado el 14 de enero de 2011 .
- ↑ "Conjuntos de datos distribuidos resilientes (RDD) - Guía de programación" . Documentación de Apache Spark . Consultado el 7 de agosto de 2025 .
- ↑ "utility-ht: Data.Bool.HT.Private" . hackage.haskell.org . Consultado el 8 de enero de 2022 .
- ↑ "The Haskell 98 Report: Standard Prelude" . www.haskell.org . Funciones booleanas . Consultado el 8 de enero de 2022 .
- 1 2 Wells, JB; Haack, C. (2002). "Branching Types". En Le Métayer, Daniel (ed.). Programming languages and systems, ESOP 2002. Lecture Notes in Computer Science. Vol. 2305. Springer. pp. 129–132 . doi : 10.1007/3-540-45927-8_9 . ISBN 978-3-540-43363-7.
- 1 2 Maessen, Jan-Willem (2002). "Eager Haskell: la ejecución con recursos limitados produce una iteración eficiente". Actas del Taller ACM SIGPLAN Haskell de 2002 (Haskell '02): Pittsburgh, Pensilvania, EE. UU.; 3 de octubre de 2002. Association for Computing Machinery. págs. 38-50. Véase la pág. 40. doi : 10.1145/581690.581694 . ISBN 978-1-58113-605-0.
- 1 2 Ejecución perezosa y especulativa Butler Lampson Microsoft Research OPODIS, Burdeos, Francia 12 de diciembre de 2006
- ↑ "¿Memoria insuficiente al asignar valores a matrices existentes?" . Respuestas de MATLAB . MATLAB Central.
- ↑ Niehren, Joachim (1996). «Computación funcional como computación concurrente» (PDF) . Actas del 23.er simposio ACM SIGPLAN-SIGACT sobre principios de lenguajes de programación - POPL '96 (PDF) . págs. 333–343 . doi : 10.1145/237721.237801 . ISBN 0897917693. S2CID 7332050 .
- ↑ Niehren, Joachim (septiembre de 2000). "Confluencia uniforme en computación concurrente" . Journal of Functional Programming . 10 (5): 453– 499. doi : 10.1017/S0956796800003762 . S2CID 66013. Consultado el 7 de enero de 2022 .
- 1 2 Stelle, George Widgery (julio de 2019). Shared-Environment Call-by-Need (PhD). Universidad de Nuevo México. pp. 11–12 . Recuperado el 8 de enero de 2022 .
- ↑ Chris Smith (22 de octubre de 2009). Programación en F# . O'Reilly Media, Inc. pág. 79. ISBN 978-0-596-15364-9Consultado el 31 de diciembre de 2010 .
- ↑ Launchbury 1993 .
- ↑ Edward Z. Yang. "Zoológico de fugas espaciales" .
- ↑ "Coincidencia de patrones perezosa - HaskellWiki" .
- ↑ "Biblioteca de rangos (desde C++20)" . cppreference.com . cppreference . Consultado el 23 de noviembre de 2025 .
- ^ Grzegorz Piwowarek, Aprovechamiento de expresiones Lambda para evaluación diferida en Java , 4Comprehension , 25 de julio de 2018.
- 1 2 Douglas W. Jones, CS:2820 Notas, Otoño 2020, Lección 25 , consultado en enero de 2021.
- ↑ Proveedor de interfaz<T> , consultado en octubre de 2020.
- ↑ Bloch, Joshua (2018). "Effective Java: Programming Language Guide" (tercera ed.). Addison-Wesley. ISBN 978-0134685991.
- ↑ "lazy | Core API – Kotlin Programming Language" . Ayuda de Kotlin . Consultado el 8 de abril de 2026 .
- ↑ "Propiedades delegadas | Kotlin" . Ayuda de Kotlin . Consultado el 8 de abril de 2026 .
- ↑ "assert | API principal – Lenguaje de programación Kotlin" . Ayuda de Kotlin . Consultado el 8 de abril de 2026 .
- ↑ "2. Funciones integradas — Documentación de Python 2.7.11" .
- ↑ "2. Funciones integradas — Documentación de Python 3.5.1" .
- ↑ "Clase Lazy(T) (Sistema)" . Microsoft.
Fuentes
- Friedman, DP ; Wise, David S. (1976). S. Michaelson; R. Milner (eds.). "Cons no debería evaluar sus argumentos" . Autómatas, lenguajes y programación, Tercer Coloquio Internacional . Edinburgh University Press.
- Henderson, Peter; Morris, James H. (1976). "Un evaluador perezoso". Actas del 3er simposio ACM SIGACT-SIGPLAN sobre principios de lenguajes de programación - POPL '76 . págs. 95–103 . doi : 10.1145/800168.811543 . S2CID 1228296 .
- Hudak, Paul (septiembre de 1989). "Concepción, evolución y aplicación de lenguajes de programación funcional". ACM Computing Surveys . 21 (3): 383– 385. doi : 10.1145/72551.72554 . S2CID 207637854 .
- Launchbury, John (1993). «Una semántica natural para la evaluación perezosa». Actas del 20.º simposio ACM SIGPLAN-SIGACT sobre principios de lenguajes de programación - POPL '93 . págs. 144-154 . doi : 10.1145/158511.158618 . ISBN 0897915607. S2CID 14945994 .
- Reynolds, John C. (1998). Teorías de los lenguajes de programación . Cambridge University Press . ISBN 9780521594141. Consultado el 23 de febrero de 2016 .
- Wadsworth, Christopher P. (1971). Semántica y pragmática del cálculo lambda (tesis doctoral). Universidad de Oxford.
Enlaces externos
- Macros de evaluación perezosa en Nemerle
- Cálculo lambda en las bibliotecas Boost. Archivado el 10 de junio de 2009 en la Wayback Machine en lenguaje C++.
- Evaluación perezosa en ANSI C++ escribiendo código en un estilo que utiliza clases para implementar cierres de funciones .
- Estrategia de evaluación
- Optimizaciones del compilador
- Implementación de lenguajes de programación funcional
- Comparación de lenguajes de programación