Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

So consider the following chunk of code which does not work as most people might expect it to

#cartoon example
a <- c(3,7,11)
f <- list()

#manual initialization
f[[1]]<-function(x) a[1]+x
f[[2]]<-function(x) a[2]+x
f[[3]]<-function(x) a[3]+x

#desired result for the rest of the examples
f[[1]](1)
# [1] 4
f[[3]](1)
# [1] 12

#attempted automation
for(i in 1:3) {
   f[[i]] <- function(x) a[i]+x
}

f[[1]](1)
# [1] 12
f[[3]](1)
# [1] 12

Note that we get 12 both times after we attempt to "automate". The problem is, of course, that i isn't being enclosed in the function's private environment. All the functions refer to the same i in the global environment (which can only have one value) since a for loop does not seem to create different environment for each iteration.

sapply(f, environment)
# [[1]]
# <environment: R_GlobalEnv>
# [[2]]
# <environment: R_GlobalEnv>
# [[3]]
# <environment: R_GlobalEnv>

So I though I could get around with with the use of local() and force() to capture the i value

for(i in 1:3) {
   f[[i]] <- local({force(i); function(x) a[i]+x})
}

f[[1]](1)
# [1] 12
f[[3]](1)
# [1] 12

but this still doesn't work. I can see they all have different environments (via sapply(f, environment)) however they appear to be empty (ls.str(envir=environment(f[[1]]))). Compare this to

for(i in 1:3) {
   f[[i]] <- local({ai<-i; function(x) a[ai]+x})
}

f[[1]](1)
# [1] 4
f[[3]](1)
# [1] 12

ls.str(envir=environment(f[[1]]))
# ai :  int 1
ls.str(envir=environment(f[[3]]))
# ai :  int 3

So clearly the force() isn't working like I was expecting. I was assuming it would capture the current value of i into the current environment. It is useful in cases like

#bad
f <- lapply(1:3, function(i) function(x) a[i]+x)
#good
f <- lapply(1:3, function(i) {force(i); function(x) a[i]+x})

where i is passed as a parameter/promise, but this must not be what's happening in the for-loop.

So my question is: is possible to create this list of functions without local() and variable renaming? Is there a more appropriate function than force() that will capture the value of a variable from a parent frame into the local/current environment?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
255 views
Welcome To Ask or Share your Answers For Others

1 Answer

This isn't a complete answer, partly because I'm not sure exactly what the question is (even though I found it quite interesting!).

Instead, I'll just present two alternative for-loops that do work. They've helped clarify the issues in my mind (in particular by helping me to understand for the first time why force() does anything at all in a call to lapply()). I hope they'll help you as well.

First, here is one that's a much closer equivalent of your properly function lapply() call, and which works for the same reason that it does:

a <- c(3,7,11)
f <- list()

## `for` loop equivalent of:
## f <- lapply(1:3, function(i) {force(i); function(x) a[i]+x})
for(i in 1:3) {
    f[[i]] <- {X <- function(i) {force(i); function(x) a[i]+x}; X(i)}
}
f[[1]](1)
# [1] 4

Second, here is one that does use local() but doesn't (strictly- or literally-speaking) rename i. It does, though, "rescope" it, by adding a copy of it to the local environment. In one sense, it's only trivially different from your functioning for-loop, but by focusing attention on i's scope, rather than its name, I think it helps shed light on the real issues underlying your question.

a <- c(3,7,11)
f <- list()

for(i in 1:3) {
   f[[i]] <- local({i<-i; function(x) a[i]+x})
}
f[[1]](1)
# [1] 4

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...