Почему локальный ({...}) должен быть определен с использованием двух раундов цитирования выражения?

Я пытаюсь понять, как R local функция работает. С его помощью вы можете открыть временную локальную область, что означает, что происходит в local (в частности, определения переменных), остается в local, Только последнее значение блока возвращается во внешний мир. Так:

x <- local({
    a <- 2
    a * 2
}) 

x
## [1] 4

a
## Error: object 'a' not found

local определяется так:

local <- function(expr, envir = new.env()){
    eval.parent(substitute(eval(quote(expr), envir)))
}

Насколько я понимаю, происходит два раунда цитирования выражения и последующей оценки:

  1. eval(quote([whatever expr input]), [whatever envir input]) генерируется как неоцененный вызов substitute,
  2. Звонок оценивается в localКадр вызывающего абонента (который в нашем случае - Глобальная среда), так[whatever expr input] оценивается в [whatever envir input]

Тем не менее, я не понимаю, почему шаг 2 является обязательным. Почему я не могу просто определить local как это:

local2 <- function(expr, envir = new.env()){
    eval(quote(expr), envir)
}

Я думаю, что это оценивает выражение expr в пустой среде? Таким образом, любая переменная, определенная в expr должен существовать в envir и поэтому исчезают после окончания local2?

Однако, если я попробую это, я получу:

x <- local2({
    a <- 2
    a * 2
}) 
x
## [1] 4
a
## [1] 2

Так a утечки в глобальную окружающую среду. Почему это?

РЕДАКТИРОВАТЬ: Еще более загадочным: Почему это не происходит для:

eval(quote({a <- 2; a*2}), new.env())
## [1]  4
a
## Error: object 'a' not found

1 ответ

Решение

Параметры в функции R передаются как обещания. Они не оцениваются, если не указано значение. Так посмотри на

# clean up first
if exists("a") rm(a)

f <- function(x) print(1)
f(a<-1)
# [1] 1
a
# Error: object 'a' not found
g <- function(x) print(x)
g(a<-1)
# [1] 1
a
# [1] 1

Обратите внимание, что в g() функция, мы используем значение, переданное функции, которая является этим назначением a так что создает a в глобальной среде. С f(), эта переменная никогда не создается, потому что этот параметр функции остается обещанием, а конец никогда не оценивался.

Если вы хотите получить доступ к параметру без его оценки, вам нужно использовать что-то вроде match.call() или же subsititute(), local() Функция делает последнее.

Если вы удалите eval.parent()вы увидите, что подстановка помещает необработанное выражение из параметра в новый вызов eval(),

h <- function(expr, envir = new.env()){
    substitute(eval(quote(expr), envir))
}
h(a<-1)
# eval(quote(a <- 1), new.env())

Где, как будто вы делаете

j<- function(x) {
  quote(x)
}
j(a<-1)
# x

вы на самом деле не создаете новый вызов функции. Дальше больше, когда ты eval() это выражение, вы запускаете оценку x из исходной среды вызова (запускающей оценку обещания), а не оценки выражения в новой среде.

local() затем использует eval.parent() так что вы можете использовать существующие переменные в среде вашего блока. Например

b<-5
local({
  a <- b
  a * 2
})
# [1] 10

Посмотрите на поведение здесь

local2 <- function(expr, envir = new.env()){
    eval(quote(expr), envir)
}
local2({a<-5; a})
# [1] 5
local2({a<-5; a}, list(a=100, expr="hello"))
# [1] "hello"

Посмотрите, как, когда мы используем непустую среду, eval() смотрит вверх expr в среде это не оценка вашего блока кода в среде.

Другие вопросы по тегам