如何运行一个环境中的任意表达式,将所有结果存储在环境中?
How to run an arbitrary expression in an environment, storing all results in the environment?
在 R 中,运行使用表达式 x <- 1
在全局环境中定义了一个变量 x
,其值为 1
。在函数中执行相同操作会在函数环境中定义变量。
使用rlang::with_env
,我们也可以在任意环境下做同样的事情:
e <- new.env()
rlang::with_env(e, {
x <- 1
y <- 2
f <- function(x) print(x)
g <- function() f(1)
})
e$x
#> [1] 1
e$g()
#> [1] 1
由 reprex package (v2.0.1)
于 2021-10-26 创建
但是,我不知道如何在函数中执行相同的操作。也就是说,一个函数接收表达式,然后 运行 在空白环境中发送它们,返回环境:
set_in_env <- function(expr) {
e <- new.env()
# q <- rlang::enquo(expr)
# z <- quote(expr)
# rlang::with_env(e, substitute(expr))
# rlang::with_env(e, parse(text = substitute(expr)))
# rlang::with_env(e, q)
# rlang::with_env(e, rlang::eval_tidy(q))
# rlang::with_env(e, z)
# rlang::with_env(e, eval(z))
rlang::with_env(e, expr)
rlang::with_env(e, {x <- 1})
return(e)
}
e <- set_in_env({y <- 2})
rlang::env_print(e)
#> <environment: 0000000014678340>
#> parent: <environment: 0000000014678730>
#> bindings:
#> * x: <dbl> <-- ONLY `x` WAS SET, NOT `y`!
也就是说,函数被赋予表达式y <- 2
,在新环境中应该是运行。出于演示目的,该函数还在环境中内部设置 x <- 1
。
无论我尝试过什么,环境都是用e$x
创建的,从未定义e$y <- 2
(注释掉的代码是其他失败的尝试)。
我相信这是可以做到的,我只是遗漏了一些东西。那么,有人可以帮助我吗?
奇怪的是 with_env
函数似乎不允许将表达式注入到表达式参数中。这是解决方法
set_in_env <- function(expr) {
e <- new.env()
q <- rlang::enexpr(expr)
rlang::inject(rlang::with_env(e, !!q))
rlang::with_env(e, {x <- 1})
return(e)
}
我们明确地使用 rlang::inject
将表达式注入到调用中,然后 inject
也会对其求值。
这可能是 base
解决方案:
set_in_env <- function(expr) {
e <- new.env()
# Resolve the given 'expr'ession as a 'call', before evaluating that call in the
# environment 'e'. Otherwise, 'expr' will first be evaluated within 'set_in_env()',
# with such consequences as described below.
eval(expr = substitute(expr), envir = e)
# ^^^^ ^^^^^^^^^^
# 'eval()' with 'substitute()'
# Avoid evaluating anything whatsoever about the 'x <- 1' assignment, until doing so
# in the environment 'e'. Otherwise, 'x <- 1' will first be evaluated within
# 'set_in_env()', and 'x' will be available in 'set_in_env()' yet unavailable in the
# environment 'e'.
evalq(expr = {x <- 1}, envir = e)
# ^^^^^
# 'evalq()' on its own
return(e)
}
当我们按照您的问题 set_in_env()
进行测试时
e <- set_in_env({y <- 2})
rlang::env_print(e)
我们得到了想要的结果:
<environment: 0000013E34E1E0D0>
parent: <environment: 0000013E34E1E488>
bindings:
* x: <dbl>
* y: <dbl>
1) 我们可以这样使用 eval/substitute:
f <- function(expr) {
eval(substitute({
expr
x <- 1
}), e <- new.env())
e
}
# test
e <- f( {y <- 2} )
ls(e)
## [1] "x" "y"
2) 或者我们可以像这样在 f 中重用 environment/frame:
f <- function(expr) {
eval(substitute({
expr
x <- 1
}))
rm(expr)
environment()
}
e <- f( {y <- 2} )
ls(e)
## [1] "x" "y"
我将其作为 issue in the rlang
GitHub 提出,其中包括其他评论(包括他打算弃用 with_env
)@lionel 给出了一种非常简洁的方法:
library(rlang)
set_in_env <- function(expr) {
e <- env()
expr <- rlang::enexpr(expr)
rlang::eval_bare(expr, e)
return(e)
}
e <- set_in_env({y <- 2})
e$y
#> [1] 2
由 reprex package (v2.0.1)
于 2021-10-27 创建
我实际上已经尝试 eval_tidy
使用 quosures,我需要的是 eval_bare
使用表达式。
在 R 中,运行使用表达式 x <- 1
在全局环境中定义了一个变量 x
,其值为 1
。在函数中执行相同操作会在函数环境中定义变量。
使用rlang::with_env
,我们也可以在任意环境下做同样的事情:
e <- new.env()
rlang::with_env(e, {
x <- 1
y <- 2
f <- function(x) print(x)
g <- function() f(1)
})
e$x
#> [1] 1
e$g()
#> [1] 1
由 reprex package (v2.0.1)
于 2021-10-26 创建但是,我不知道如何在函数中执行相同的操作。也就是说,一个函数接收表达式,然后 运行 在空白环境中发送它们,返回环境:
set_in_env <- function(expr) {
e <- new.env()
# q <- rlang::enquo(expr)
# z <- quote(expr)
# rlang::with_env(e, substitute(expr))
# rlang::with_env(e, parse(text = substitute(expr)))
# rlang::with_env(e, q)
# rlang::with_env(e, rlang::eval_tidy(q))
# rlang::with_env(e, z)
# rlang::with_env(e, eval(z))
rlang::with_env(e, expr)
rlang::with_env(e, {x <- 1})
return(e)
}
e <- set_in_env({y <- 2})
rlang::env_print(e)
#> <environment: 0000000014678340>
#> parent: <environment: 0000000014678730>
#> bindings:
#> * x: <dbl> <-- ONLY `x` WAS SET, NOT `y`!
也就是说,函数被赋予表达式y <- 2
,在新环境中应该是运行。出于演示目的,该函数还在环境中内部设置 x <- 1
。
无论我尝试过什么,环境都是用e$x
创建的,从未定义e$y <- 2
(注释掉的代码是其他失败的尝试)。
我相信这是可以做到的,我只是遗漏了一些东西。那么,有人可以帮助我吗?
奇怪的是 with_env
函数似乎不允许将表达式注入到表达式参数中。这是解决方法
set_in_env <- function(expr) {
e <- new.env()
q <- rlang::enexpr(expr)
rlang::inject(rlang::with_env(e, !!q))
rlang::with_env(e, {x <- 1})
return(e)
}
我们明确地使用 rlang::inject
将表达式注入到调用中,然后 inject
也会对其求值。
这可能是 base
解决方案:
set_in_env <- function(expr) {
e <- new.env()
# Resolve the given 'expr'ession as a 'call', before evaluating that call in the
# environment 'e'. Otherwise, 'expr' will first be evaluated within 'set_in_env()',
# with such consequences as described below.
eval(expr = substitute(expr), envir = e)
# ^^^^ ^^^^^^^^^^
# 'eval()' with 'substitute()'
# Avoid evaluating anything whatsoever about the 'x <- 1' assignment, until doing so
# in the environment 'e'. Otherwise, 'x <- 1' will first be evaluated within
# 'set_in_env()', and 'x' will be available in 'set_in_env()' yet unavailable in the
# environment 'e'.
evalq(expr = {x <- 1}, envir = e)
# ^^^^^
# 'evalq()' on its own
return(e)
}
当我们按照您的问题 set_in_env()
进行测试时
e <- set_in_env({y <- 2})
rlang::env_print(e)
我们得到了想要的结果:
<environment: 0000013E34E1E0D0>
parent: <environment: 0000013E34E1E488>
bindings:
* x: <dbl>
* y: <dbl>
1) 我们可以这样使用 eval/substitute:
f <- function(expr) {
eval(substitute({
expr
x <- 1
}), e <- new.env())
e
}
# test
e <- f( {y <- 2} )
ls(e)
## [1] "x" "y"
2) 或者我们可以像这样在 f 中重用 environment/frame:
f <- function(expr) {
eval(substitute({
expr
x <- 1
}))
rm(expr)
environment()
}
e <- f( {y <- 2} )
ls(e)
## [1] "x" "y"
我将其作为 issue in the rlang
GitHub 提出,其中包括其他评论(包括他打算弃用 with_env
)@lionel 给出了一种非常简洁的方法:
library(rlang)
set_in_env <- function(expr) {
e <- env()
expr <- rlang::enexpr(expr)
rlang::eval_bare(expr, e)
return(e)
}
e <- set_in_env({y <- 2})
e$y
#> [1] 2
由 reprex package (v2.0.1)
于 2021-10-27 创建我实际上已经尝试 eval_tidy
使用 quosures,我需要的是 eval_bare
使用表达式。