r语言 - 如何在环境中运行任意表达式,并将所有结果存储在环境中?



在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包(v2.0.1)在2018-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函数似乎不允许向expression参数中注入表达式。这里有一个关于

的例子
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中重用环境/框架:

f <- function(expr) {
eval(substitute({
expr
x <- 1
}))
rm(expr)
environment()
}
e <- f( {y <- 2} )
ls(e)
## [1] "x" "y"

我在rlangGitHub中提出了这个问题,在其他评论中(包括他打算弃用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包(v2.0.1)在2018-10-27创建

我实际上已经尝试了eval_tidy与报价,我需要的是eval_bare与表达式。

最新更新