【问题标题】:Enclosing variables within for loop在 for 循环中包含变量
【发布时间】:2014-11-21 18:21:58
【问题描述】:

因此,请考虑以下代码块,它不像大多数人所期望的那样工作

#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

请注意,在我们尝试“自动化”之后,我们两次都得到 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 循环中发生的事情。

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

【问题讨论】:

  • 您能否澄清一下为什么您对变量重命名不满意我的意思是解决方案:f[[i]] &lt;- local({ai&lt;-i; function(x) a[ai]+x})
  • 与其说让我不开心,倒不如说这是唯一的方法,我不明白为什么这是唯一的方法。为什么一定要改名?有没有办法进行设置,这样我就不必重命名(我只想像在lapply 版本中那样获取具有相同名称的变量的副本)。这更有意义吗?

标签: r for-loop lazy-evaluation


【解决方案1】:

这不是一个完整的答案,部分原因是我不确定这个问题到底是什么(尽管我发现它很有趣!)。

相反,我将只介绍两个可行的替代for-loops。他们帮助澄清了我心中的问题(特别是帮助我第一次理解为什么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-loop 只是微不足道的不同,但通过将注意力集中在 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

【讨论】:

  • 我认为这两个都准确地捕捉到了我想要做的事情。事实上,我以为我已经尝试过i&lt;-i 方法,但在我的测试中一定遇到了一个不相关的错误。我想重要的部分是force() 不做任何类型的捕获/分配,它只是评估承诺。至少我目前是这样理解的。
  • @MrFlick -- 以下是我对force() 角色的看法。我个人对force(i) 在你的for 循环中不起作用并不感到惊讶,因为就像你说的那样,它只是评估i。 (force 毕竟只是 function(x) x 的另一个名字!)没有分配给任何符号,返回的值就消失在以太中。
  • 当你有嵌套函数(如在你的lapply() 调用或我的for-loop 等效函数中)时,情况会有所不同。在这种情况下,被引用的i 是提供给标准 R 函数的参数,因此,它是作为一个 Promise 启动的。当它随后在顶级函数的主体中 force()'d 时,i 的当前值(最终从 lapply()for-loop 的索引中获得)被绑定到承诺.由于 R 的词法作用域,该绑定值是内部函数在查找 i 的值时最终找到并使用的值。
  • 我也是这么想的。感谢您花时间把它写清楚。这启发了我创建函数:capture &lt;- function(...) {vars&lt;-sapply(substitute(...()), deparse); pf &lt;- parent.frame(); Map(assign, vars, mget(vars, envir=pf, inherits = TRUE), MoreArgs=list(envir=pf))},然后我可以像 for(i in 1:3) {f[[i]] &lt;- local({capture(i); function(x) a[i]+x})} 一样使用它(希望我没有重新发明已经存在的东西)。
【解决方案2】:

这种方法对你有用吗?

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

这是一种构建函数的低级方式,但它确实“按您的意愿”工作。

【讨论】:

    【解决方案3】:

    force() 的替代方案可以在 for 循环本地环境中工作

    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 回答中的 cmets)

    【讨论】:

      猜你喜欢
      • 2011-07-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-12
      • 2014-02-07
      • 2017-07-11
      • 2021-09-06
      • 2016-12-28
      相关资源
      最近更新 更多