Как работает опция 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,

Обратите внимание, что родительская среда отличается от родительского фрейма:

  • родительская среда определяется тем, где была определена функция, поэтому, если функция была определена в глобальной среде, то ее родительская среда является глобальной средой.
  • родительский фрейм - это окружение вызывающего
Другие вопросы по тегам