Explicar una peculiaridad de evaluación perezosa

Estoy leyendo el libro de Hadith Wickhams sobre Github, en particular esta parte sobre evaluación perezosa . Ahí da un ejemplo de las consecuencias de la evaluación perezosa, en la parte con funciones add/adders . Permítanme citar ese bit:

Esta [evaluación diferida] es importante cuando se crean cierres con lapply o un bucle:

 add <- function(x) { function(y) x + y } adders <- lapply(1:10, add) adders[[1]](10) adders[[10]](10) 

x se evalúa perezosamente la primera vez que llama a una de las funciones del sumdor. En este punto, el ciclo está completo y el valor final de x es 10. Por lo tanto, todas las funciones del sumdor agregarán 10 a su entrada, ¡probablemente no lo que usted quería! Forzar manualmente la evaluación corrige el problema:

 add <- function(x) { force(x) function(y) x + y } adders2 <- lapply(1:10, add) adders2[[1]](10) adders2[[10]](10) 

No parece entender eso, y la explicación es mínima. ¿Podría alguien explicar ese ejemplo en particular y explicar qué sucede allí? Estoy específicamente desconcertado por la oración “en este punto, el ciclo está completo y el valor final de x es 10”. ¿Qué loop? ¿Qué valor final, dónde? Debe ser algo simple que me falta, pero simplemente no lo veo. Muchas gracias por adelantado.

El objective de:

 adders <- lapply(1:10, function(x) add(x) ) 

es crear una lista de funciones de add , la primera agrega 1 a su entrada, la segunda agrega 2, etc. La evaluación lenta hace que R espere realmente para crear las funciones de sumdores hasta que realmente comiences a llamar a las funciones. El problema es que después de crear la primera función del sumdor, x aumenta con el ciclo de lapply , terminando con un valor de 10. Cuando llama a la primera función del sumdor, la evaluación lenta ahora crea la función, obteniendo el valor de x . El problema es que la x original ya no es igual a uno, sino al valor al final del ciclo de lapply , es decir, 10.

Por lo tanto, la evaluación diferida ocasiona que todas las funciones del sumdor esperen hasta que se lapply ciclo de lapply la lapply la función. Luego construyen su función con el mismo valor, es decir, 10. La solución que Hadley sugiere es forzar que x se evalúe directamente, evitando la evaluación diferida y obteniendo las funciones correctas con los valores de x correctos.

Esto ya no es cierto a partir de R 3.2.0!

La línea correspondiente en el registro de cambios dice:

Las funciones de orden superior como las funciones de aplicación y Reduce () ahora fuerzan los argumentos a las funciones que aplican para eliminar las interacciones indeseables entre la evaluación diferida y la captura de variables en los cierres.

Y de hecho:

 add <- function(x) { function(y) x + y } adders <- lapply(1:10, add) adders[[1]](10) # [1] 11 adders[[10]](10) # [1] 20