3

在我的 R 开发中,我需要将函数原语包装在proto对象中,以便在$perform()调用对象的方法时可以将许多参数自动传递给函数。函数调用在内部通过do.call(). 一切都很好,除非函数试图从定义它的闭包中访问变量。在这种情况下,该函数无法解析名称。

这是我发现的重现该行为的最小示例:

library(proto)

make_command <- function(operation) {
  proto(
    func = operation,
    perform = function(., ...) {
      func <- with(., func) # unbinds proto method
      do.call(func, list(), envir=environment(operation))
    }  
    )
}

test_case <- function() {
  result <- 100  
  make_command(function() result)$perform()
}

# Will generate error:
# Error in function ()  : object 'result' not found
test_case()

我有一个可重复的testthat测试,它也输出大量的诊断输出。诊断输出让我难过。通过查找父环境链,我的位于函数内部的诊断代码找到并打印了函数无法找到的同一个变量。请参阅此要点。.

如何do.call正确设置环境?

4

2 回答 2

3

这是与发帖者线下讨论后的最终答案:

make_command <- function(operation) {
 proto(perform = function(.) operation())
}
于 2012-06-20T23:55:32.547 回答
1

我认为这里的问题更清晰,更容易探索,如果你:

  • make_command()用命名函数替换其中的匿名函数。

  • 使该函数打开 a browser()(而不是尝试获取result)。这样你就可以环顾四周,看看你在哪里以及发生了什么。

试试这个,它应该澄清你的问题的原因:

test_case <- function() {
  result <- 100  
  myFun <- function() browser()
  make_command(myFun)$perform()
}
test_case()
## Then from within the browser:
#
parent.env(environment())
# <environment: 0x0d8de854>
# attr(,"class")
# [1] "proto"       "environment"
get("result", parent.env(environment()))
# Error in get("result", parent.env(environment())) : 
#   object 'result' not found
#
parent.frame()
# <environment: 0x0d8ddfc0>
get("result", parent.frame())  ## (This works, so points towards a solution.)
# [1] 100

这就是问题所在。尽管您认为您正在评估myFun(),其环境是 的评估框架test_case(),但您的调用do.call(func, ...)实际上是评估func(),其环境是proto定义它的环境。result在自己的框架中寻找和没有找到之后,调用func()遵循词法作用域的规则,然后在proto环境中寻找。及其父环境都不包含名为 的对象result,从而导致您收到错误消息。

如果这不能立即生效,您可以继续在浏览器中四处寻找。以下是一些您可能会觉得有帮助的进一步电话:

environment(get("myFun", parent.frame()))
ls(environment(get("myFun", parent.frame())))
environment(get("func", parent.env(environment())))
ls(environment(get("func", parent.env(environment()))))
于 2012-06-20T04:52:51.737 回答