【问题标题】:R scope: force variable substitution in function without local environmentR范围:在没有本地环境的函数中强制变量替换
【发布时间】:2013-03-26 01:18:43
【问题描述】:

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

示例:一组函数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,...

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

为了解决这个问题,我可以使用 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 个函数,但通常不可扩展)

【问题讨论】:

  • R 中的每个函数都有一个关联的环境:你为什么担心它?
  • 另外,你不能通过使用闭包来绕过它(你已经这样做了),你可以通过使用 force 来绕过它。

标签: r scope


【解决方案1】:

这里有一些使用body&lt;-的方法

你可以使用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))
}

【讨论】:

  • 不错!就像第二个一样。干杯!
  • 我不会那么担心不使用body -- 哎呀,Mathematica 的一半都是关于修改函数中的对象 :-)
  • @hadley -- 删除了parse。我希望没有太多的小猫受苦,我承认这是我的第一个想法。
【解决方案2】:

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

但使用原始闭包确实没有缺点。

【讨论】:

    【解决方案3】:

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

    正式

    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-02-27
      • 1970-01-01
      • 1970-01-01
      • 2019-09-05
      • 2019-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多