我确实鼓励您阅读有关词法作用域的内容,但我认为避免编写许多变量的好方法可能是:
get_args_for <- function(fun, env = parent.frame(), inherits = FALSE, ..., dots) {
potential <- names(formals(fun))
if ("..." %in% potential) {
if (missing(dots)) {
return(as.list(env))
}
else if (!is.list(dots)) {
stop("If provided, 'dots' should be a list.")
}
potential <- setdiff(potential, "...")
}
args <- mget(potential, env, ..., ifnotfound = list(NULL), inherits = inherits)
args <- args[sapply(args, Negate(is.null))]
c(args, dots)
}
f_a <- function(b, c = 0, ..., d = 1) {
b <- b + 1
c(b = b, c = c, d = d, ...)
}
f_e <- function() {
b <- 2
c <- 2
arg_list <- get_args_for(f_a, dots = list(5))
do.call(f_a, arg_list)
}
> f_e()
b c d
3 2 1 5
默认情况下设置 inherits = FALSE
确保我们只从指定的环境中获取变量。
在调用 get_args_for
时,我们也可以将 dots = NULL
设置为不传递所有变量,
但将省略号留空。
然而,这仍然不完全健壮,
因为 dots
只是简单地附加在结尾处,
如果某些参数没有命名,则可能被按位置匹配。
此外,如果一些值在调用中应该是 NULL
,那么很难检测到它们。
我强烈建议不要在 R 包内使用以下内容。
不仅代码看起来很丑,
还会收到 R 的 CMD 检查关于未定义全局变量的提示。
其他选项。
f_a <- function() {
return(b + c)
}
f_e <- function() {
b <- 2
c <- 2
# replace f_a's enclosing environment with the current evaluation's environment
environment(f_a) <- environment()
d <- f_a()
d
}
> f_e()
[1] 4
类似上述内容的代码可能无法在R程序包中运行,因为我认为程序包中的函数会锁定其封闭环境。
或者:
f_a <- function() {
with(parent.frame(), {
b + c
})
}
f_e <- function() {
b <- 2
c <- 2
f_a()
}
> f_e()
[1] 4
这样,您就不会永久修改其他函数的封闭环境。但是,两个函数将共享一个环境,因此可能会发生这样的情况:
f_a <- function() {
with(parent.frame(), {
b <- b + 1
b + c
})
}
f_e <- function() {
b <- 2
c <- 2
d <- f_a()
c(b,d)
}
> f_e()
[1] 3 5
调用内部函数会修改外部环境中的值。
另一种稍微灵活一些的选择是使用 eval
临时仅修改封闭环境。
但是,有些 R 函数通过“黑魔法”检测它们当前的执行环境,
无法被 eval
欺骗;
请参见这个讨论。
f_a <- function() {
b <- b + 1
b + c
}
f_e <- function() {
b <- 2
c <- 2
d <- eval(body(f_a), list(), enclos=environment())
c(b=b, d=d)
}
> f_e()
b d
2 5