R:使省略号中的命名项在(可能嵌套的)执行环境中可用

R: Make named items in ellipsis available in (maybe nested) execution environment

我希望能够在函数的参数中传递命名对象,这些对象已与省略号(...,AKA 点)相匹配,以便在该函数的执行环境中普遍可用,或者在任何定义的环境中执行的函数,就好像参数是在那里输入的一样。

我尝试这样做,对于一个函数,一个在该函数外部定义的嵌套函数,以及一个在该函数内部定义的嵌套函数,使用 list2env(),它应该 return 元素它的参数列表进入 parent.frame() 环境,我知道这是调用环境。因此:

# Ellipsis in nested functions
inner_f1<- function(x){list2env(list(...)[-1L]); x + b}

outer_f <- function(x, ...){
  list2env(list(...)[-1L])
  in01 <- x + a
  inner_f2 <- function(x){list2env(list(...)[-1L]); print(ls()); x + c}
  in02 <- inner_f2(x)  
  in03 <- inner_f1(x)
  out <- list(in01, in02, in03)
}

outer_f(x=0, a=1, b=2, c=3)

我在嵌套函数的定义中尝试了使用和不使用 ...,但都不起作用。所需的输出将是:

$ in01
[1] 1
$ in02
[1] 2
$ in03
[1] 3

"dots" 下的 R 帮助文件没有提供有关将 ... 值传递给内部函数的信息,它提到从 ... 中获取信息的唯一方法是通过 ..(n ) 方法。它指的是 "An Introduction to R," 但 par 示例似乎错误地暗示,内部函数拥有自己的 ... 就足够了,尽管 par 代码(未在此处引用)通过复杂的方式获取内容things to args = list(...),R 语言定义也描述了 list(...) 方法。我没有找到成语 substitute(list(...))[-1],经常在 R 基础包中使用,在任何地方都有正式记录,但无论如何,这和 eval(substitute(alist(...) )) 来自 "Advanced R"::Nonstandard Evaluation 似乎符合我的要求。

在 Whosebug 上有很多关于 ... 和嵌套函数问题的答案,但我阅读的所有 15 个左右似乎都比我正在寻找的通用方法更专业。

请注意,可用的变量与 ls() 列出的变量不同。特别是,ls() 不会列出父环境中的变量,但父环境中的变量仍可用作输入(如果使用 <<-,也可用作输出)。我们假设您只需要可用的变量而不关心 ls()。 (如果您确实想将外部函数执行环境中的变量实际注入到内部函数中,则将 ... 传递给内部函数,并对外部函数使用与此处所示相同的方法。)以下示例表明,虽然 b可访问它未显示在 ls().

的输出中
f1 <- function() { 
  b <- 1
  f2 <- function() { 
    print(b)  # will print the value of b showing b is accessible
    print(ls()) # no variables will be shown
  }
  f2() 
}
f1()

给予:

[1] 1
character(0)

现在回到这个问题,这里有一些备选方案:

1) 用 试试with:

inner_fun0 <- function() in1

outer_fun <- function(...) with(list(...), {
  inner_fun <- function() in1
  environment(inner_fun0) <- environment()
  list(in1, inner_fun(), inner_fun0())
})
outer_fun(in1 = 7)

给予:

[[1]]
[1] 7

[[2]]
[1] 7

[[3]]
[1] 7

2) list2env 另一种方法是像这样使用 list2env

outer_fun2 <- function(...) {
  list2env(list(...), environment())
  inner_fun <- function() in1
  environment(inner_fun0) <- environment()
  list(in1, inner_fun(), inner_fun0())
}
outer_fun2(in1 = 7)

给予:

[[1]]
[1] 7

[[2]]
[1] 7

[[3]]
[1] 7

您可以使用

中的想法构建其他变体

还可以使用 proto 包将所有这些重铸为面向对象的框架。

备选方案:

# note how I add an argument to specify the exact environment:
inner_f1<- function(x, env, ...){
  with(env, {list2env(list(...)); x + b})
 }

outer_f <- function(x, ...){

  # get the "address", i.e. the environment:
  here <- environment()

  # specify the environment in list2env:
  list2env(list(...), envir = here)
  in01 <- x + a

  inner_f2 <- function(x){list2env(list(...), envir = here); print(ls()); x + c}
  # alternatively, use: list2env(list(...), envir = parent.frame(n=2))

  in02 <- inner_f2(x)  

  in03 <- inner_f1(x, here)

  out <- list(in01, in02, in03)
  return(out)
}

outer_f(x=0, a=1, b=2, c=3)

[1] "x"
[[1]]
[1] 1

[[2]]
[1] 3

[[3]]
[1] 2

本质上,您需要确保正确的 "address" 可用于所有功能。