R范围:在没有本地环境的情况下强制执行变量替换

时间:2013-03-26 01:18:44

标签: r scope

我在循环中定义函数并尝试强制评估循环变量,而不必携带私有环境。

示例:一组函数handlers$h1handlers$h2,...,handlers$h6只需将1,2,...,6传递给另一个函数,如下所示:

handlers <- list()
for (i in 1:6) {
    handlers[[paste0('h', i)]] <- function () {
        message(i) # <-- example
    }
}

所以handlers$h1()应该留言1,handlers$h2()留言2,......

相反,所有函数都返回6i当前值。

为了解决这个问题,我可以使用一个闭包specified in this question

msg <- function(i) {
    force(i)
    function () { message(i) }
}

for (i in 1:6) {
    handlers[[paste0('h', i)]] <- msg(i)
}

现在每个函数都按预期工作,每个函数都必须随身携带:

handlers$h1
# function () { message(i) }
# <environment: 0x9342b80>

如何使handlers$h1打印function () { message(1) },即评估i并将其直接替换为定义,从而消除对环境的需求?

我能想到的唯一方法是:

  • 使用eval(我不想做的事);
  • 直接用1-6替换手写出每个定义(在这种情况下只有6个函数很好,但通常不可扩展)

3 个答案:

答案 0 :(得分:7)

以下是一些使用body<-

的方法

您可以使用bquote

handlers <- list()

for (i in 1:6) {
  handlers[[paste0('h', i)]] <- function () {}
  body( handlers[[paste0('h', i)]]) <- bquote(message(.(i)))
}

handlers$h1
## function () 
##   message(1L)

substitute

for (i in 1:6) {
  handlers[[paste0('h', i)]] <- function () {}
  body( handlers[[paste0('h', i)]]) <- substitute(message(i), list(i=i))
}

答案 1 :(得分:3)

不幸的是,基地R缺乏手工制作功能的功能,但pryr供应make_function

library(pryr)

handlers <- list()
for (i in 1:6) {
  body <- substitute(message(i), list(i = i))
  f <- make_function(alist(), body)

  handlers[[paste0('h', i)]] <- f
}

请注意使用substitute手动修改引用的电话。

pryr中的另一个很酷的(IMO!)函数是unenclose,它通过替换封闭环境中定义的变量来取消函数:

msg <- function(i) {
    force(i)
    function () message(i)
}
msg(1)
# function () message(i)
# <environment: 0x102dc6ca0>
unenclose(msg(1))
# function () 
# message(1)

但是使用原始封口确实没有任何缺点。

答案 2 :(得分:1)

这有两种方法。它们是相同的,除了每个中的##行:

<强>缩甲醛&LT; -

handlers <- list()
f <- function() message(i)
for (i in 1:6) { 
   formals(f) <- list(i = i) ##
   handlers[[paste0('h', i)]] <- f 
}

<强>微量

handlers <- list()
f <- function() message(i)
for (i in 1:6) { 
   trace(f, bquote(i <- .(i)), print = FALSE) ##
   handlers[[paste0('h', i)]] <- f 
}