4

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

示例:一组函数handlers$h1, handlers$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 也应如此,...

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

为了解决这个问题,我可以使用这个问题中指定的闭包

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 个函数,但通常不可扩展)
4

3 回答 3

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))
}
于 2013-03-26T01:39:17.483 回答
3

不幸的是,base 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)

但是他们真的没有使用原始封闭的缺点。

于 2013-03-26T13:30:29.733 回答
1

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

形式<-

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 
}
于 2013-03-26T13:55:48.463 回答