查找调用的{(点)参数的原始环境

时间:2017-06-27 06:08:29

标签: r rlang

我希望能够找到呼叫的...(点)参数所源自的环境。

方案

例如,考虑一个函数

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()应该返回...(嵌套)调用中的foo(...)所在的环境:这是调用bar(1, 2)的环境,因为2(但不是1)传递给foo的点。特别是,我们应该得到

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

env_dots()

的天真实施

更新 - 此处定义的env_dots() 一般都可以正常工作,因为最终...可能会被参数填充在调用堆栈的多个级别调用。

以下是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. rlang 和/或基础R 中实施env_dots()是否有更为巧妙的方法?

  2. 如何将match.call()调用移至env_dots()内? match.call(sys.function(-1), call = sys.call(-1), expand.dots = FALSE)确实有用。

  3. 备注 - 人们无法从rlang::quos(...)推断出点的原始环境,因为某些状态不会赋予调用环境(例如,当表达式为文字对象)。

1 个答案:

答案 0 :(得分:1)

很抱歉找到一个老问题,但是我不确定所需的行为是否明确。 ...不是单个表达式;这是一个表达式列表。对于rlang的情况,每个表达式都有自己的环境。那么列表的环境应该是什么?

此外,...列表本身可以修改。考虑以下示例,其中g取其...,为它加上一个(未求值的)表达式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

请注意,进入f的三个指标都有其自己的环境。 (正如您在问题中指出的那样,10之类的文字有一个空环境。这是因为该值是相同的,而与在哪个环境中求值无关。)

在这种情况下,假设env_dots()f()内部被调用应该返回什么?