将 `=` 运算符中的 LHS 替换为 Sys.setenv 中的 rlang tidyeval

substitute LHS in `=` operator with rlang tidyeval inside Sys.setenv

问题描述

Sys.setenv 没有提供 LHS(环境变量名称)作为参数的简单接口。如果要动态定义应该设置什么环境变量,则需要元编程方法。

基本R方式

这个小辅助函数按预期工作。

setenv = function(var, value, quiet=TRUE) {
  stopifnot(is.character(var), !is.na(var), length(value)==1L, is.atomic(value))
  qc = as.call(c(list(quote(Sys.setenv)), setNames(list(value), var)))
  if (!quiet) print(qc)
  eval(qc)
}

var_name = "RISCOOL"
Sys.getenv(var_name)
#[1] ""
setenv(var_name, value=150, quiet=FALSE)
#Sys.setenv(RISCOOL = 150)
Sys.getenv(var_name)
#[1] "150"

问题

问题是如何使用 pryr or rlang (tidyeval) 之类的软件包解决问题?或者最终是另一个流行的。
我根本不了解这些包,希望更好地了解它们如何简化我的元编程代码。

请注意,问题是关于元编程的,设置环境变量只是一个例子。

只需使用do.call

lst <- structure(list(value), names=name)
do.call(Sys.setenv, lst)

使用do.call:

var_name = "RISCOOL"
do.call("Sys.setenv", as.list(setNames(3, var_name)))

# check that it worked
Sys.getenv(var_name)
## [1] "3"

或使用 purrr

library(purrr)
invoke("Sys.setenv", set_names(4, var_name))

如果要使用rlang风格的准引用构造调用直接求值,需要blast()

blast <- function(expr, env = caller_env()) {
  eval_bare(enexpr(expr), env)
}

vars <- c(A = "a", B = "b", C = "c")

blast(data.frame(!!!vars))
#>   A B C
#> 1 a b c

在您的原始示例中,您需要取消对名称的引用。我们尚不支持在 := 的 LHS 上进行深度取消引用(请参阅 https://github.com/r-lib/rlang/issues/279),但您可以改用 !!!

setenv <- function(var, value) {
  args <- setNames(value, var)
  blast(Sys.setenv(!!!args))
}

setenv("foobar", 1)
#> [1] TRUE

Sys.getenv("foobar")
#> [1] "1"

要插入打印的调用,blast 级别太高,但您可以使用以下组件:

setenv <- function(var, value, quiet = FALSE) {
  args <- setNames(value, var)
  call <- expr(Sys.setenv(!!!args))

  if (!quiet) {
    print(call)
  }

  # Evaluate in our own environment where `Sys.setenv()` is defined
  # (and protected if we're in a package namespace)
  eval(call)
}

我认为你需要使用 :=。 它的用法在 dplyr vignettes 之一中进行了解释, 但功能由 rlang 提供。 在这种情况下,您可以使用 call2:

setenv <- function(var, val) {
  rlang::call2("Sys.setenv", !!rlang::enexpr(var) := val)
}

setenv(foo, "bar")
# Sys.setenv(foo = "bar")

只需根据需要添加一个 eval 调用即可。