4

我希望能够找到...调用的(点)参数源自的环境。

设想

例如,考虑一个函数

foo <- function(x, ...) {
  # do something
}

我们想要一个env_dots()从内部调用的函数,它可以在对的调用foo()中找到 的原始环境,即使对 的调用是深度嵌套的。也就是说,如果我们定义...foo()foo()

foo <- function(x, ...) {
  # find the originating environment of '...'
  env <- env_dots()

  # do something
}

并嵌套一个对 的调用foo,就像这样,

baz <- function(...) {
  a <- "You found the dots"
  bar(1, 2)
}
bar <- function(...)
  foo(...)

那么调用baz()应该返回...in (嵌套)调用foo(...)发起的环境:这是进行调用的环境bar(1, 2),因为2(而不是1)被传递给foo. 特别是,我们应该得到

baz()$a
#> [1] "You found the dots"

天真的实现env_dots()

Updateenv_dots()在这里定义的一般不会起作用,因为 final...可能由在调用堆栈的多个级别调用的参数填充。

这是一种可能性env_dots()

# mc: match.call() of function from which env_dots() is called
env_dots <- function(mc) {
  # Return NULL if initial call invokes no dots
  if (!rlang::has_name(mc, "...")) return(NULL)

  # Otherwise, climb the call stack until the dots origin is found
  stack <- rlang::call_stack()[-1]
  l <- length(stack)
  i <- 1
  while (i <= l && has_dots(stack[[i]]$expr)) i <- i + 1
  # return NULL if no dots invoked
  if (i <= l) stack[[i + 1]]$env else NULL
}

# Does a call have dots?
has_dots <- function(x) {
  if (is.null(x))
    return(FALSE)
  args <- rlang::lang_tail(x)
  any(vapply(args, identical, logical(1), y = quote(...)))
}

这似乎有效:与

foo <- function(x, ...)
  env_dots(match.call(expand.dots = FALSE))

我们得到

baz()$a
#> [1] "You found the dots"

bar(1, 2)  # 2 gets passed down to the dots of foo()
#> <environment: R_GlobalEnv>

bar(1)     # foo() captures no dots
#> NULL

问题

上面的实现env_dots()效率不是很高。

  1. env_dots()rlang和/或base R中是否有更巧妙的实现方式?

  2. 如何将match.call()调用移至 inside env_dots() match.call(sys.function(-1), call = sys.call(-1), expand.dots = FALSE)确实会起作用。

备注— 无法从 中推断点的起源环境rlang::quos(...),因为某些 quosures 不会被赋予调用环境(例如,当表达式是文字对象时)。

4

1 回答 1

1

很抱歉提出一个老问题,但我不确定所需的行为是否明确。...不是一个单一的表达式;这是一个表达式列表。在rlangquosures 的情况下,这些表达式中的每一个都有自己的环境。那么list的环境应该是怎样的呢?

此外,...可以修改列表本身。考虑以下示例, whereg接受它的...,在它前面加上一个(未计算的)表达式x+3并将它传递给f

f <- function(...) {rlang::enquos( ... )}

g <- function(...) {
  a <- rlang::quo( x + 3 )
  l <- rlang::list2( a, ... )
  f(!!!l)
}

b <- rlang::quo( 5 * y )

g( b, 10 )
# [[1]]
# <quosure>
#   expr: ^x + 3
#   env:  0x7ffd1eca16f0

# [[2]]
# <quosure>
#   expr: ^5 * y
#   env:  global

# [[3]]
# <quosure>
#   expr: ^10
#   env:  empty

请注意,这三个 quosures 中的每一个f都有自己的环境。(正如您在问题中指出的那样,文字10有一个空环境。这是因为值是相同的,与它在哪个环境中评估无关。)

鉴于这种情况,在内部调用时假设env_dots()返回什么f()

于 2018-10-23T01:21:19.580 回答