在 do.call 调用的函数中查找局部变量
Look up local variable in function called by do.call
我有一个函数,它应该根据调用它的环境使用一个值:
Foo <- function(a) {
return(a + b)
}
在全局环境中我现在可以简单地做
b <- 1
Foo(2)
# 3
真正的函数将接受一个参数列表,所以我使用 do 调用,很简单:
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
这个returns3
,我要5
。如果我没有定义或删除全局 b
函数将失败,因为它找不到符号。
我尝试使用 quote = TRUE
为 do.call
方法(environment()
、parent.env()
、...)提供不同的环境,但无济于事。
有没有办法强制 Foo
通过将其作为环境传递来查找本地块中的变量?
你应该使用复杂的赋值运算符<<-
,因为你想在函数执行环境的父环境中更改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
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())
它也可以查看父框架的祖先)。
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
5) 另一种方法是使用 trace
向 Foo 中注入一条语句,然后再将其删除。
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
6) 如果我们将 Foo 的主体包裹在 eval.parent(substitute({...})) 中,这将有效地将它注入给它的调用者访问 b。另请参阅从 R News 1/3 第 11 页开始的 Thomas Lumley 文章。
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
7) 这实际上与引擎盖下的 (6) 相同,只是它包装得很好。这是这里唯一使用包的。
library(gtools)
Foo7 <- defmacro(a, expr = {
return(a + b)
})
local({
b <- 3
print(Foo7(2))
print(do.call(Foo7, list(a = 2)))
})
## [1] 5
## [1] 5
我有一个函数,它应该根据调用它的环境使用一个值:
Foo <- function(a) {
return(a + b)
}
在全局环境中我现在可以简单地做
b <- 1
Foo(2)
# 3
真正的函数将接受一个参数列表,所以我使用 do 调用,很简单:
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
这个returns3
,我要5
。如果我没有定义或删除全局 b
函数将失败,因为它找不到符号。
我尝试使用 quote = TRUE
为 do.call
方法(environment()
、parent.env()
、...)提供不同的环境,但无济于事。
有没有办法强制 Foo
通过将其作为环境传递来查找本地块中的变量?
你应该使用复杂的赋值运算符<<-
,因为你想在函数执行环境的父环境中更改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
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())
它也可以查看父框架的祖先)。
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
5) 另一种方法是使用 trace
向 Foo 中注入一条语句,然后再将其删除。
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
6) 如果我们将 Foo 的主体包裹在 eval.parent(substitute({...})) 中,这将有效地将它注入给它的调用者访问 b。另请参阅从 R News 1/3 第 11 页开始的 Thomas Lumley 文章。
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
7) 这实际上与引擎盖下的 (6) 相同,只是它包装得很好。这是这里唯一使用包的。
library(gtools)
Foo7 <- defmacro(a, expr = {
return(a + b)
})
local({
b <- 3
print(Foo7(2))
print(do.call(Foo7, list(a = 2)))
})
## [1] 5
## [1] 5