在for循环中包含变量

时间:2014-09-26 16:59:52

标签: r for-loop lazy-evaluation

因此,请考虑以下大部分代码,这些代码无法像大多数人所希望的那样工作

#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

请注意,在我们尝试&#34;自动化&#34;后,我们都会得到12次。当然,问题是i并未包含在函数的私有环境中。所有函数在全局环境中引用相同的i(它只能有一个值),因为for循环似乎不会为每次迭代创建不同的环境。

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

所以我虽然可以使用local()force()来捕捉i

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

但这仍然无法奏效。我可以看到它们都有不同的环境(通过sapply(f, environment)),但它们似乎是空的(ls.str(envir=environment(f[[1]])))。将此与

进行比较
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

很明显force()并没有像我期待的那样工作。我假设它会将i的当前值捕获到当前环境中。它在像

这样的情况下很有用
#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})

其中i作为参数/承诺传递,但这绝不是for-loop中发生的事情。

所以我的问题是:是否可以在没有local()和变量重命名的情况下创建这个函数列表?是否有比force()更合适的函数,它将从父框架中捕获变量的值到本地/当前环境中?

3 个答案:

答案 0 :(得分:4)

这不是一个完整的答案,部分是因为我不确定究竟是什么问题(即使我发现它很有趣!)。

相反,我只提出两个可行的for - 循环。他们帮助澄清了我脑海中的问题(尤其是帮助我第一次理解force()在调用lapply()时做了什么。我希望他们也会帮助你。

首先,这是一个与正确的函数lapply()调用更接近的函数,它的作用与它的作用相同:

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

其次,这是一个使用local()但没有(严格地或字面地说)重命名i的人。但它确实通过将其副本添加到本地环境来“重新屏蔽”它。从某种意义上说,它与你的功能for循环只是微不足道,但是通过关注i的范围,而不是它的名称,我认为它有助于揭示你的潜在问题。问题

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

答案 1 :(得分:1)

这种方法对你有用吗?

ff<-list()
for(i in 1:3) {
    fillit <- parse(text=paste0('a[',i,']+x' ))
   ff[[i]] <- function(x) ''
   body(ff[[i]])[1]<-fillit
}

它是一种构建函数的低级方法,但它确实可以“按照你的意愿”工作。

答案 2 :(得分:0)

可以在for循环本地环境中使用的force()的替代方法是

capture <- function(...) {
    vars<-sapply(substitute(...()), deparse); 
    pf <- parent.frame(); 
    Map(assign, vars, mget(vars, envir=pf, inherits = TRUE), MoreArgs=list(envir=pf))
 }

然后可以像

一样使用
for(i in 1:3) {
    f[[i]] <- local({capture(i); function(x) a[i]+x})
}

(取自Josh上面回答的评论)