1

在 R 中,运行表达式会在全局环境中x <- 1定义一个值为 的变量。在函数中执行相同操作会在函数的环境中定义变量。x1

使用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 包于 2021-10-26 创建(v2.0.1)

但是,我不知道如何在函数中做同样的事情。也就是说,一个函数接收表达式,然后在空白环境中运行它们,返回环境:

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(注释掉的代码是其他失败的尝试)。

我相信这是可以做到的,而且我只是错过了一些东西。那么,有人可以帮帮我吗?

4

4 回答 4

1

这可能是一个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>
于 2021-10-26T20:36:06.033 回答
1

奇怪的是,该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也会对其进行评估。

于 2021-10-26T20:02:24.753 回答
0

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"
于 2021-10-27T02:26:41.377 回答
0

我在GitHub中将此作为一个问题rlang提出,其中包括其他评论(包括他打算弃用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 包于 2021-10-27 创建(v2.0.1)

我实际上尝试eval_tidy过 quosures,我需要的是eval_bare表达式。

于 2021-10-27T19:31:39.963 回答