lazy-evaluation - lazy - evaluacion perezosa python
Llamar por necesidad vs llamar por nombre (5)
Call-by-name es una función de disciplina de llamada donde, cuando se realiza una llamada a una función de recepción, en lugar de los argumentos que se evalúan, foo recibe (detrás de la escena) un objeto apropiado que le permitirá evaluar los parámetros. necesariamente; o de forma equivalente, la evaluación progresa por macrocambio. Si se requiere un parámetro más de una vez, se evaluará más de una vez. Ver: http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_name
La llamada por necesidad es muy similar, excepto que el objeto que se pasa es una promesa y se evaluará no más de una vez; en referencias posteriores a un parámetro, se utiliza el valor memorizado. Ver: http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_need
No entendía la diferencia entre Llamada por nombre y Llamada por necesidad. Como entendí, el método de llamada por necesidad restaura la respuesta devuelta. Pero, ¿cómo nos ayuda y hay alguna diferencia fundamental entre los resultados?
Por ejemplo,
begin integer n;
procedure foo(e, n);
integer e, n;
begin
for n := 1 step 1 until 10 do begin
prints(`;;; the value of e is '');
printnln(e)
end
end;
foo(2 * n, n)
end
Así que en llamada por nombre, como entendí, obtendremos:
;;; the value of e is 2
;;; the value of e is 4
;;; the value of e is 8
y así. Esto se debe a que pasamos 2*n
a e
, y e
se evalúa con el nuevo i
cada vez. ¿Qué pasaría en call-by-need?
En llamada por necesidad, entramos en el bucle y evaluamos el valor solo una vez. Entonces, en el código anterior, copiaremos (2*n)
dentro del bucle (estilo macro) y evaluaremos la expresión solo una vez (no como llamada por nombre). Entonces, en la primera iteración obtendremos e=2
. este será el valor de e
también en la siguiente iteración, y la salida será:
;;; the value of e is 2
;;; the value of e is 2
;;; the value of e is 2
En primer lugar, la llamada por necesidad o la llamada por nombre representan formas de implementar la evaluación perezosa, por lo que es necesario saber qué es lazy-evaluation ...
Puede ver las llamadas por necesidad en Haskell, por ejemplo, y las llamadas por nombre en Scala.Call-by-need es quizás la forma esperada de implementar la pereza. La primera vez que "realmente" necesita el valor, lo calculó y creó un caché del valor calculado para el acceso futuro.
cuando tiene llamada por nombre , no realiza este tipo de almacenamiento en caché, evalúa los argumentos de la función cada vez que se usan en el cuerpo de la función. Es posible pensar que no tiene sentido, pero es útil implementar alguna estructura de control de flujo en función del lenguaje, por ejemplo, un momento.
def myWhile (cond : => Boolean, body : => Unit) {
if (cond) { body ; myWhile (cond, body) }
}
Y luego puedes llamar a la función myWhile,
var x = 3
myWhile (x != 0, {
print (x)
x = x - 1
})
Si tuviéramos llamada por nombre para el ejemplo anterior, la expresión "cond" no se almacena en caché y se evalúa cada vez que se necesita.
Parece que tu confusión proviene del hecho de que estás pensando en un contexto imperative . La discusión de llamada por necesidad frente a llamada por valor surge principalmente sobre lenguajes declarativos y funcionales, y el cálculo lambda.
Puede ver en este artículo acerca de las estrategias de evaluación que tanto la llamada por nombre como la llamada por necesidad se consideran estrategias de evaluación perezosas . La evaluación perezosa significa que cuando una expresión se pasa como un parámetro a una función, no se evalúa antes de ingresar al cuerpo de la función, sino solo cuando se accede / lee la primera vez dentro de la función. Si el resultado de tal expresión nunca se usa en el interior, entonces nunca será evaluado.
Por ejemplo el ? :
? :
operador es perezoso en Java como lo demuestra el siguiente código:
String test(Object obj)
{
return 1 == 2 ? obj.toString() : "Hello World";
}
test(null); // this won''t throw a NullPointerException
Call-by-need es una característica esencial de la mayoría de los lenguajes funcionales que tienen un subconjunto puro . En un lenguaje puramente funcional , cada función debe ser referencialmente transparente , es decir, no puede tener side-effects . Tales funciones puras tienen la propiedad de que para una entrada determinada siempre devuelven la misma salida, sin importar cuántas veces se llame, y que nunca cambian nada en el "estado del mundo". Se comportan como funciones matemáticas escritas en el papel.
Como ya se ha dado cuenta, la estrategia de llamada por necesidad no es factible al llamar a funciones que no son puras, ya que lo más probable es que esté interesado en los efectos secundarios debido a las llamadas consecutivas. Por otro lado, se convierte en una característica esencial para el rendimiento cuando se utiliza en lenguajes puramente funcionales (consulte el segundo ejemplo a continuación). Además, vea estas páginas de wiki sobre los conceptos de Reducción de gráficos y Memoization .
Ejemplos del mundo real
Primero. Un ejemplo de un sistema comúnmente usado que usa reducción de gráficos es Apache Ant . La hormiga no evalúa un objetivo dos veces. Este diseño lo hace conveniente para esbozar un plan de construcción declarativo.
Segundo. Si desea ver una buena demostración de memoización, escriba este código Haskell en un intérprete de GHC y vea qué sucede:
Prelude> let fibs = 0:1:(zipWith (+) fibs (tail fibs))
-- This defines the Fibonacci sequence.
Prelude> fibs !! 200000
-- Prints the 200,000th Fibonacci number,
-- takes several seconds to calculate.
Prelude> fibs !! 200000
-- Prints the same number as before,
-- but this time it returns immediately.
Nota. Es posible que también hayas oído hablar de la estrategia de evaluación de llamada por valor . En contraste con la llamada por nombre y la llamada por necesidad , la llamada por valor es una estrategia de evaluación estricta. Es como llamada por nombre en el sentido de que llamar varias veces resulta en una evaluación múltiple. Este es el paradigma más común para los programadores que están acostumbrados a lenguajes imperativos como C # o Java.
Tanto la llamada por nombre como la llamada por necesidad son como la expansión de la macro. Pero en la llamada por necesidad, una vez que se evalúa el valor, se memoriza el valor y se usa este valor la próxima vez. para por ejemplo: -hay una pregunta en la puerta 2003.
global int i=100,j=5;
void P(x){
int i=10;
printf("%d",x+10);
i=200;
j=20;
printf("%d",x);}
main(){P(i+j);}
LLAMADA POR EL NOMBRE: en lugar de x en la función, se convierte en i + j, por lo que la primera impresión será i + j + 10 = 10 + 5 + 10 = 25 y la próxima impresión será i + j = 200 + 20
salida: 25,220
LLAMADA POR NECESIDAD: en lugar de x, se convierte en i + j y después de la primera impresión, x, es decir, el valor de i + j se convierte en 15 y se memoriza. por lo que la salida será
salida: 25,15