查找调用的 ...(点)参数的原始环境
Finding the origin environment of the ... (dots) arguments of a call
我希望能够找到发起调用的 ...
(点)参数的环境。
场景
例如,考虑一个函数
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()
应该 return 对 foo(...)
的(嵌套)调用中的 ...
发起的环境:这是调用 bar(1, 2)
被生成,因为 2
(而不是 1
)被传递给 foo
的点。特别是,我们应该得到
baz()$a
#> [1] "You found the dots"
env_dots()
的简单实现
Update — env_dots()
,如此处所定义,通常 not 工作,因为最终的 ...
可能由在调用堆栈的多个级别调用的参数填充。
这是 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()
的实现效率不是很高
在rlangand/orbase R[=82中有没有更巧妙的方法实现env_dots()
=]?
如何将 match.call()
调用移动到 env_dots()
内?
match.call(sys.function(-1), call = sys.call(-1), expand.dots = FALSE)
确实有效。
Remark — 无法从 rlang::quos(...)
推断出圆点的起源环境,因为一些 quosures 不会被赋予调用环境(例如,当表达式是文字对象时)。
很抱歉翻出一个老问题,但我不确定所需的行为是否定义明确。 ...
不是单个表达式;这是一个表达式列表。在 rlang
quosures 的情况下,每个表达式都有自己的环境。那么list的环境应该是怎样的呢?
此外,...
列表本身是可以修改的。考虑以下示例,其中 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()
return 在 f()
内部调用时应该怎么办?
我希望能够找到发起调用的 ...
(点)参数的环境。
场景
例如,考虑一个函数
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()
应该 return 对 foo(...)
的(嵌套)调用中的 ...
发起的环境:这是调用 bar(1, 2)
被生成,因为 2
(而不是 1
)被传递给 foo
的点。特别是,我们应该得到
baz()$a
#> [1] "You found the dots"
env_dots()
的简单实现
Update — env_dots()
,如此处所定义,通常 not 工作,因为最终的 ...
可能由在调用堆栈的多个级别调用的参数填充。
这是 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()
的实现效率不是很高
在rlangand/orbase R[=82中有没有更巧妙的方法实现
env_dots()
=]?如何将match.call()
调用移动到env_dots()
内?match.call(sys.function(-1), call = sys.call(-1), expand.dots = FALSE)
确实有效。
Remark — 无法从 rlang::quos(...)
推断出圆点的起源环境,因为一些 quosures 不会被赋予调用环境(例如,当表达式是文字对象时)。
很抱歉翻出一个老问题,但我不确定所需的行为是否定义明确。 ...
不是单个表达式;这是一个表达式列表。在 rlang
quosures 的情况下,每个表达式都有自己的环境。那么list的环境应该是怎样的呢?
此外,...
列表本身是可以修改的。考虑以下示例,其中 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()
return 在 f()
内部调用时应该怎么办?