Как работает опция en.vir do.call?
Документация do.call
состояния:
Если
quote
являетсяFALSE
, по умолчанию, то аргументы оцениваются (в вызывающей среде, а не вenvir
).
Это предложение подсказало бы мне, что когда quote = FALSE
, указав envir
не имеет значения. Тем не менее, это не тот случай, и на самом деле я встречал случаи, когда мне нужно указать envir
чтобы заставить функцию работать.
Простейший воспроизводимый пример:
g1 <- function(x) {
args <- as.list(match.call())
args[[1]] <- NULL # remove the function call
do.call(print, args, quote = FALSE) # call print()
}
g2 <- function(x) {
args <- as.list(match.call())
args[[1]] <- NULL # remove the function call
do.call(print, args, quote = FALSE, envir = parent.frame()) # call print(), specifying envir
}
h1 <- function(x, y) {
g1(x*y)
}
h2 <- function(x, y) {
g2(x*y)
}
С этими функциями, h2()
ведет себя так, как можно было бы подумать, но h1()
не:
h1(2, 3)
#Error in print(x) : object 'y' not found
h2(2, 3)
#[1] 6
y <- 100
h1(2, 3)
#[1] 600
## Looks like g1() took the value of y from the global environment
h2(2, 3)
#[1] 6
Кто-нибудь может объяснить мне, что здесь происходит?
Примечание: здесь есть связанный пост, но, читая ответы, я не могу точно указать, что do.call()
делает с envir
переменная.
1 ответ
?do.call
говорит:
Envir
среда, в которой можно оценить вызов. Это будет наиболее полезно, если символьная строка и аргументы являются символами или выражениями в кавычках.
Мы можем легко проиллюстрировать это, если what=
аргумент do.call
это строка символов затем envir=
определяет, где это ищется.
e <- new.env()
e$f <- function() 2
f <- function() 3
do.call("f", list())
## [1] 3
do.call("f", list(), envir = e)
## [1] 2
То же самое верно для аргументов, как показывает код в вопросе. Обратите внимание, что аргументы уже цитируются, так как match.call()
используется.
Что происходит в случае h1
а также g1
является то, что это эффективно работает в g1
do.call(print, list(call("*", quote(x), quote(y))), quote = FALSE)
Теперь он находит x
в g1
(поскольку g1
имеет один аргумент x
) но нет y
в g1
так выглядит родительская среда g1
которая является глобальной средой, где она находит y
,
В случае h2
а также g2
это работает в g2
:
do.call(print, list(call("*", quote(x), quote(y))), quote = FALSE, envir = parent.frame())
и он находит x
а также y
в h2
который является родителем g2
,
Обратите внимание, что родительская среда отличается от родительского фрейма:
- родительская среда определяется тем, где была определена функция, поэтому, если функция была определена в глобальной среде, то ее родительская среда является глобальной средой.
- родительский фрейм - это окружение вызывающего