R:替换绑定在所有父环境中的变量
R: Substitute variables bound in all parent environments
函数 base::substitute(expr, env)
,根据其文档页面,
returns the parse tree for the (unevaluated) expression expr, substituting any variables bound in env.
我正在寻找一种方法来替换不是在一个特定环境中绑定的任何变量,而是在当前调用堆栈中的所有环境中,即通过迭代 parent.frame(i)
遇到的所有环境,其中i
在 seq_len(sys.nframe())
中。此外,我希望应用标准范围规则。
这是一个矛盾:R 中的标准作用域是词法的,但我在这里描述的是动态作用域(感谢@MikkoMarttila 帮助我解决了这个问题)。我真正想要的是一种替代任何变量的方法,该变量不是在一个特定环境中绑定,而是在所有父封闭环境中绑定,可以通过重复应用 base::parent.env()
.
来枚举其集合
考虑以下示例:
do_something <- function(todo) {
cat(
paste(
deparse(substitute(todo, environment())),
collapse = "\n"
)
)
}
nested_do <- function() {
var_2 <- "goodbye"
do_something({
print(var_1)
print("world")
print(var_2)
})
}
var_1 <- "hello"
nested_do()
目前这给
print(var_1)
print("world")
print(var_2)
我想要的地方
print("hello")
print("world")
print("goodbye")
我查看了 base::bquote()
和 rlang::enexpr()
,但对于这两者,我必须用 .()
或 !!
显式标记 substitution/unquoting 的变量。我宁愿不必手动指定变量,而是解决所有找到的问题(就像在 base::substitute()
中一样)。此外,我尝试将 base::substitute()
与相应的 env
参数进行迭代应用,并且我查看了 oshka::expand()
,但我没有尝试过任何我需要的东西。
非常感谢任何帮助。
其他上下文
我想要实现的目标如下:我在集群 运行 LSF 上工作。这意味着我可以使用提交工具 bsub
提交作业,该工具可以将 R 文件作为输入。现在我想要一个生成这些输入文件的脚本(例如使用函数 do_something()
)。
long_running_fun <- function(x) {
Sys.sleep(100)
x / 2
}
var_1 <- 2 + 2
var_2 <- var_1 + 10
do_something({
print(var_1)
var_3 <- long_running_fun(var_2)
print(var_3)
})
在上述情况下,我希望将以下内容(或等效内容)写入文件
print(4)
var_3 <- long_running_fun(14)
print(var_3)
与其这样做,我建议您像这样传递环境:
esubstitute <- function(expr, envir) do.call("substitute", list(expr, envir))
do_something <- function(todo, envir = parent.frame()) {
cat(
paste(
deparse(esubstitute(todo, envir)),
collapse = "\n"
)
)
}
nested_do <- function(envir = parent.frame()) {
var_2 <- "goodbye"
do_something({
print(var_1)
print("world")
print(var_2)
}, envir)
}
var_1 <- "hello"
nested_do()
给予:
[1] "hello"
[1] "world"
[1] "goodbye"
"goodbye">
您可能还想查看 envnames 包。
您可以定义一个函数来执行这样的替换序列:
也就是说,取一个表达式并将其替换为所有
调用堆栈中的环境。这是一种方法:
substitute_stack <- function(expr) {
expr <- substitute(expr)
# Substitute in all envs in the call stack
envs <- rev(sys.frames())
for (e in envs) {
expr <- substitute_q(expr, e)
}
# sys.frames() does not include globalenv() and
# substitute() doesnt "substitute" there
e <- as.list(globalenv())
substitute_q(expr, e)
}
# A helper to substitute() in a pre-quoted expression
substitute_q <- function(expr, env = parent.frame()) {
eval(substitute(substitute(x, env), list(x = expr)))
}
让我们试一试:
do_something <- function(todo) {
cat(
paste(
deparse(substitute_stack(todo)),
collapse = "\n"
)
)
}
nested_do <- function() {
var_2 <- "goodbye"
do_something({
print(var_1)
print("world")
print(var_2)
})
}
var_1 <- "hello"
nested_do()
#> {
#> print("hello")
#> print("world")
#> print("goodbye")
#> }
实际上这样做是否是个好主意是另一个问题。
可能更受欢迎。
由 reprex package (v0.2.0.9000) 创建于 2018-07-19。
在 的基础上,我认为以下内容符合我的要求
do_something <- function(todo) {
# A helper to substitute() in a pre-quoted expression
substitute_q <- function(expr, env) {
eval(substitute(substitute(x, env), list(x = expr)))
}
substitute_parents <- function(expr) {
expr <- substitute(expr)
# list all parent envs
envs <- list()
env <- environment()
while (!identical(env, globalenv())) {
envs <- c(envs, env)
env <- parent.env(env)
}
# substitute in all parent envs
for (e in envs) {
expr <- substitute_q(expr, e)
}
# previously did not include globalenv() and
# substitute() doesnt "substitute" there
e <- as.list(globalenv())
substitute_q(expr, e)
}
cat(
paste(
deparse(substitute_parents(todo)),
collapse = "\n"
)
)
}
这给出了
nested_do <- function() {
var_2 <- "not_this"
do_something({
print(var_1)
Sys.sleep(100)
print("world")
print(var_2)
})
}
var_1 <- "hello"
var_2 <- "goodbye"
do_something({
print(var_1)
Sys.sleep(100)
print("world")
print(var_2)
})
#> {
#> print("hello")
#> Sys.sleep(100)
#> print("world")
#> print("goodbye")
#> }
nested_do()
#> {
#> print("hello")
#> Sys.sleep(100)
#> print("world")
#> print("goodbye")
#> }
函数 base::substitute(expr, env)
,根据其文档页面,
returns the parse tree for the (unevaluated) expression expr, substituting any variables bound in env.
我正在寻找一种方法来替换不是在一个特定环境中绑定的任何变量,而是在当前调用堆栈中的所有环境中,即通过迭代 parent.frame(i)
遇到的所有环境,其中i
在 seq_len(sys.nframe())
中。此外,我希望应用标准范围规则。
这是一个矛盾:R 中的标准作用域是词法的,但我在这里描述的是动态作用域(感谢@MikkoMarttila 帮助我解决了这个问题)。我真正想要的是一种替代任何变量的方法,该变量不是在一个特定环境中绑定,而是在所有父封闭环境中绑定,可以通过重复应用 base::parent.env()
.
考虑以下示例:
do_something <- function(todo) {
cat(
paste(
deparse(substitute(todo, environment())),
collapse = "\n"
)
)
}
nested_do <- function() {
var_2 <- "goodbye"
do_something({
print(var_1)
print("world")
print(var_2)
})
}
var_1 <- "hello"
nested_do()
目前这给
print(var_1)
print("world")
print(var_2)
我想要的地方
print("hello")
print("world")
print("goodbye")
我查看了 base::bquote()
和 rlang::enexpr()
,但对于这两者,我必须用 .()
或 !!
显式标记 substitution/unquoting 的变量。我宁愿不必手动指定变量,而是解决所有找到的问题(就像在 base::substitute()
中一样)。此外,我尝试将 base::substitute()
与相应的 env
参数进行迭代应用,并且我查看了 oshka::expand()
,但我没有尝试过任何我需要的东西。
非常感谢任何帮助。
其他上下文
我想要实现的目标如下:我在集群 运行 LSF 上工作。这意味着我可以使用提交工具 bsub
提交作业,该工具可以将 R 文件作为输入。现在我想要一个生成这些输入文件的脚本(例如使用函数 do_something()
)。
long_running_fun <- function(x) {
Sys.sleep(100)
x / 2
}
var_1 <- 2 + 2
var_2 <- var_1 + 10
do_something({
print(var_1)
var_3 <- long_running_fun(var_2)
print(var_3)
})
在上述情况下,我希望将以下内容(或等效内容)写入文件
print(4)
var_3 <- long_running_fun(14)
print(var_3)
与其这样做,我建议您像这样传递环境:
esubstitute <- function(expr, envir) do.call("substitute", list(expr, envir))
do_something <- function(todo, envir = parent.frame()) {
cat(
paste(
deparse(esubstitute(todo, envir)),
collapse = "\n"
)
)
}
nested_do <- function(envir = parent.frame()) {
var_2 <- "goodbye"
do_something({
print(var_1)
print("world")
print(var_2)
}, envir)
}
var_1 <- "hello"
nested_do()
给予:
[1] "hello"
[1] "world"
[1] "goodbye"
"goodbye">
您可能还想查看 envnames 包。
您可以定义一个函数来执行这样的替换序列: 也就是说,取一个表达式并将其替换为所有 调用堆栈中的环境。这是一种方法:
substitute_stack <- function(expr) {
expr <- substitute(expr)
# Substitute in all envs in the call stack
envs <- rev(sys.frames())
for (e in envs) {
expr <- substitute_q(expr, e)
}
# sys.frames() does not include globalenv() and
# substitute() doesnt "substitute" there
e <- as.list(globalenv())
substitute_q(expr, e)
}
# A helper to substitute() in a pre-quoted expression
substitute_q <- function(expr, env = parent.frame()) {
eval(substitute(substitute(x, env), list(x = expr)))
}
让我们试一试:
do_something <- function(todo) {
cat(
paste(
deparse(substitute_stack(todo)),
collapse = "\n"
)
)
}
nested_do <- function() {
var_2 <- "goodbye"
do_something({
print(var_1)
print("world")
print(var_2)
})
}
var_1 <- "hello"
nested_do()
#> {
#> print("hello")
#> print("world")
#> print("goodbye")
#> }
实际上这样做是否是个好主意是另一个问题。
由 reprex package (v0.2.0.9000) 创建于 2018-07-19。
在
do_something <- function(todo) {
# A helper to substitute() in a pre-quoted expression
substitute_q <- function(expr, env) {
eval(substitute(substitute(x, env), list(x = expr)))
}
substitute_parents <- function(expr) {
expr <- substitute(expr)
# list all parent envs
envs <- list()
env <- environment()
while (!identical(env, globalenv())) {
envs <- c(envs, env)
env <- parent.env(env)
}
# substitute in all parent envs
for (e in envs) {
expr <- substitute_q(expr, e)
}
# previously did not include globalenv() and
# substitute() doesnt "substitute" there
e <- as.list(globalenv())
substitute_q(expr, e)
}
cat(
paste(
deparse(substitute_parents(todo)),
collapse = "\n"
)
)
}
这给出了
nested_do <- function() {
var_2 <- "not_this"
do_something({
print(var_1)
Sys.sleep(100)
print("world")
print(var_2)
})
}
var_1 <- "hello"
var_2 <- "goodbye"
do_something({
print(var_1)
Sys.sleep(100)
print("world")
print(var_2)
})
#> {
#> print("hello")
#> Sys.sleep(100)
#> print("world")
#> print("goodbye")
#> }
nested_do()
#> {
#> print("hello")
#> Sys.sleep(100)
#> print("world")
#> print("goodbye")
#> }