在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"
我在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包(v2.0.1)在2018-10-27创建
我实际上已经尝试了eval_tidy
与报价,我需要的是eval_bare
与表达式。