问题描述
Foo <- function(a) {
return(a + b)
}
在全局环境中我现在可以简单地做
b <- 1
Foo(2)
# 3
真正的函数将接受一个参数列表,所以我使用的是 do call,很简单:
do.call(Foo,list(a = 2)
# 3
在 local
块(或 test_that
块)中运行它这不符合我想要的方式(无论哪种方式)。
local({
b <- 3
print(Foo(2))
print(do.call(Foo,list(a = 2)))
})
# [1] 3
# [1] 3
这将返回 3
,我想要 5
。如果我没有定义或删除全局 b
,该函数将失败,因为它找不到符号。
我尝试使用 do.call
为 environment()
方法(parent.env()
、quote = TRUE
、...)提供不同的环境,但没有使用但无济于事。
有没有办法强制 Foo
通过将其作为环境传递来查找本地块中的变量?
解决方法
1) 由于 b 没有在 Foo 中定义,Foo 将在 Foo 被定义的环境中寻找 b,而不是在它被调用的环境中。
您可以重新定义 Foo 的环境。这将制作 Foo 的副本,以便在本地环境中查找其中的自由变量。不使用任何包。
local({
b <- 3
environment(Foo) <- environment()
print(Foo(2))
print(do.call(Foo,list(a = 2)))
})
## [1] 5
## [1] 5
2) 如果可以修改 Foo,那么其他可能性是通过将 b 作为附加参数传递或将环境作为附加参数传递并让 Foo 在该环境中评估 b 来修改它。
Foo2 <- function(a,b) {
return(a + b)
}
local({
b <- 3
print(Foo2(2,b))
print(do.call(Foo2,list(a = 2,b = b)))
})
## [1] 5
## [1] 5
3) 或
Foo3 <- function(a,envir = parent.frame()) {
return(a + envir$b)
}
local({
b <- 3
print(Foo3(2))
print(do.call(Foo3,list(a = 2)))
})
## [1] 5
## [1] 5
4) 上面的一个变体,只涉及修改 Foo 的签名,而不是它的主体如下(如果你想让它查看父框架的祖先也是如此)。
get("b",parent.frame())
5) 另一种方法是使用 Foo4 <- function(a,b = parent.frame()$b) {
return(a + b)
}
local({
b <- 3
print(Foo4(2))
print(do.call(Foo4,list(a = 2)))
})
## [1] 5
## [1] 5
将语句注入 Foo 中,然后将其删除。
trace
6) 如果我们将 Foo 的主体包装在 eval.parent(substitute({...})) 中,这将有效地将其注入到调用者中,使其可以访问 b。另请参阅从 R News 1/3 的第 11 页开始的 Thomas Lumley 文章。
local({
b <- 3
on.exit(untrace(Foo))
trace(Foo,bquote(b <- .(b)),print = FALSE)
print(Foo(2))
print(do.call(Foo,list(a = 2)))
})
## [1] 5
## [1] 5
7) 这实际上与引擎盖下的 (6) 相同,只是它很好地包裹了它。这是这里唯一一个使用包的。
Foo6 <- function(a) eval.parent(substitute({
return(a + b)
}))
local({
b <- 3
print(Foo6(2))
print(do.call(Foo6,list(a = 2)))
})
## [1] 5
## [1] 5
,
您应该使用复杂的赋值运算符 <<-
,因为您想在函数执行环境的父环境中更改 b
的值:
library(rlang)
local({
b <<- 3
print(Foo(2))
print(do.call(Foo,list(a = 2)))
list(env = env_parent(current_env())) # I added this so it becomes clear that this is the child of Global environment. You can omit it if you like.
})
[1] 5
[1] 5
$env
<environment: R_GlobalEnv>
我再次阅读了您的描述,看到您希望在 local
的本地环境中评估您的函数。所以就像我朋友建议的那样:
local({
b <- 3
print(Foo(2))
print(do.call(Foo,list(a = 2)))
},envir = current_env())
[1] 5
[1] 5