Я хочу найти среду, из которой исходят ...
(точки) аргументы вызова.
Сценарий
Например, рассмотрим функцию
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()
не очень эффективна.
Есть ли более эффективный способ реализовать
env_dots()
в rlang и / или base R?Как я могу переместить вызовmatch.call()
вenv_dots()
?match.call(sys.function(-1), call = sys.call(-1), expand.dots = FALSE)
действительно будет работать.
Примечание - нельзя вывести окружение происхождения точек из rlang::quos(...)
, потому что некоторые запросы не будут наделены вызывающей средой (например, когда выражение является буквальным объектом).