2012-04-04 10 views
5

Estoy intentando utilizar una serie de llamadas a lapply para crear una lista de funciones curried, que idealmente en la última llamada lapply, devuelve el valor final deseado. El currying funciona, pero lapply parece aplicar siempre el último elemento en la lista después de la segunda aplicación.¿Por qué se pierden los valores variables en cierres después de llamar repetidamente a lapply?

Ejemplo:

curry <- function(fn, ...) { 
    arglist <- list(...) 
    function(...) { 
    do.call(fn, append(arglist, list(...))) 
    } 
} 
# rcurry is used only to init the first lapply. 
rcurry <- function(v1, fn, ...) { 
    arglist <- append(list(v1), list(...)) 
    function(...) { 
    do.call(fn, append(arglist, list(...))) 
    } 
} 

myadd <- function(a,b,c) { 
    a+b+c 
} 

Esto funciona como se espera:

# you can achieve the same by closure: 
# curry.a <- lapply(c(10, 1000), FUN = function(a) { curry(myadd, a) }) 
curry.a <- lapply(list(10, 1000), rcurry, myadd) 
curry.a[[1]](1,2) 
curry.a[[2]](1,2) 

# > [1] 13 
# > [1] 1003 

El siguiente lapply de curry "Mangles del alcance":

# this does give the desired output: 
# curry.a.b <- list(curry(curry.a[[1]], 1), curry(curry.a[[2]], 1)) 
curry.a.b <- lapply(curry.a, curry, 1) 
curry.a.b[[1]](2) 
curry.a.b[[2]](2) 

# > [1] 1003 
# > [1] 1003 

No parece como una resultado de la función curry o rcurry. El uso de la función roxygen de Curry hace lo mismo. creando curry.a por cierre arriba o usando curry.a <- list(curry(myadd, 10), curry(myadd, 1000)) también resulta igual.

Y, por supuesto, el curry definitiva:

# it doesn't work if you re-define this: 
# curry.a.b <- list(curry(curry.a[[1]], 1), curry(curry.a[[2]], 2)) 
curry.a.b.c <- lapply(curry.a.b, curry, 2) 
lapply(curry.a.b.c, do.call, list()) 

# > [1] 1003 
# > [1] 1003 

lo que está pasando aquí?

+2

'' lapply' evalúa FUN' en un nuevo entorno. Creo que tiene algo que ver con eso. –

+0

Creo que la respuesta es la misma que en la respuesta de Tommy a http://stackoverflow.com/questions/9950144/access-lapply-index-names-inside-fun/9950734#comment12707459_9950734 –

+0

Joshua es cálido, kohske lo clava. Gracias a todos. –

Respuesta

2

fn en curry no se evalúa en el alcance de la función y por lo tanto es promise. Si force que entonces se puede obtener lo que espera:

curry <- function(fn, ...) { 
    force(fn) 
    arglist <- list(...) 
    function(...) { 
    do.call(fn, append(arglist, list(...))) 
    } 
} 

entonces,

> curry.a.b <- lapply(curry.a, curry, 1) 
> curry.a.b[[1]](2) 
[1] 13 
> curry.a.b[[2]](2) 
[1] 1003 
> 
> curry.a.b.c <- lapply(curry.a.b, curry, 2) 
> lapply(curry.a.b.c, do.call, list()) 
[[1]] 
[1] 13 

[[2]] 
[1] 1003 

Más internamente, lapply genera una variable local X que se hace referencia por cada llamada de la función. Si X no se evalúa en cada función al llamar al lapply, X es una promesa. Después de llamar al lapply, X en todas las llamadas de función desde lapply, devuelve el mismo valor (es decir, el último). Así lapply es similar con:

f0 <- function(i) function() i 
f1 <- function(i) {force(i); function() i} 

f <- local({ 
r0 <- list() 
r1 <- list() 
for (i in 1:2) { 
    r0[[i]] <- f0(i) 
    r1[[i]] <- f1(i) 
} 
list(r0 = r0, r1 = r1) 
}) 

entonces,

> f$r0[[1]]() 
[1] 2 
> f$r1[[1]]() 
[1] 1 
> f$r0[[2]]() 
[1] 2 
> f$r1[[2]]() 
[1] 2 
+0

100% ojo de toro. ¡Gracias! De hecho, pensé que el cierre era la promesa, así que hice 'closure <- function (...) {do.call (fn, append (arglist, list (...))}; force (closure); return closure' Por supuesto, eso no funcionó. –

Cuestiones relacionadas