#r #scope
#r #область видимости
Вопрос:
Может кто-нибудь, пожалуйста, объяснить, что происходит, когда выражение вычисляется в system.time
? В частности, почему любые переменные, объявленные в expr
аргументе, видны в глобальной среде?
Вот сокращенная версия внутренностей system.time
, которая не делает ничего, кроме вычисления выражения, которое передается функции:
st <- function(expr){
expr
}
st(aa <- 1)
aa
[1] 1
Очевидно, что результатом этого является то, что оно создает переменную aa
в глобальной среде. Это сбивает меня с толку, поскольку я думал, что присвоение переменной внутри функции делает ее локальной по области видимости.
Что здесь происходит?
Ответ №1:
Это связано с тем, что предоставленные аргументы вычисляются во фрейме вычисления вызывающей функции (как описано в разделе 4.3.3 документа определения языка R).
Выражение , заключенное пользователем в system.time()
, является предоставленным аргументом , который сопоставляется позиционно expr
. Затем, когда expr
вычисление принудительно выполняется в теле system.time
, оно вычисляется в фрейме оценки вызывающей функции. Если system.time()
было вызвано из .GlobalEnv
, то именно там будут выполняться любые назначения, которые являются частью expr
.
Редактировать:
Вот пример, показывающий, что expr
это вычисляется в глобальной среде, если это предоставленный (но не используемый по умолчанию) аргумент.
st2 <- function(expr = newVar <- 33){
expr
}
# Using the default argument -- eval and assignment
# within evaluation frame of the function.
st2()
newVar
Error: object 'newVar' not found
# Using a supplied argument -- eval and assignment
# within the calling function's evaluation frame (here .GlobalEnv)
st2(newVar <- 44)
newVar
# [1] 44
Комментарии:
1. Я могу поверить вам на слово, но начислю бонусные баллы, если вы сможете найти ссылку в руководствах R. 🙂
2. Ошибаюсь, ваш ответ ( 1) кажется моей интуиции гораздо более ясным 🙂
3. @Andrie — я хотел поблагодарить вас за интересный вопрос. Считается ли моя супер-слабая ссылка на определение языка R ссылкой на руководства R? 😉
4. 1 Ваша ссылка на руководства совсем не слабая и очень полезная. Спасибо.
Ответ №2:
РЕДАКТИРОВАТЬ: согласно комментарию @Tommy’s: Оценка фактически выполняется только после использования аргумента expr (это отложенная оценка).
Передается языковой объект, а не выражение. По сути, вы вкладываете <-
функцию (с двумя аргументами) в вызов функции st(), и результат <-
вызова передается в st . Как вы можете видеть в ?assignOps
, <-
функция возвращает присвоенное значение автоматически. Как @Josh уже говорил вам, эта оценка вложенной функции выполняется в среде, из которой вызывается функция.
То, что вы делаете, эквивалентно
st(mean(1:10))
Чтобы увидеть разницу, вы можете сделать:
st <- function(expr){
typeof(expr)
}
> st(aa <- 1)
[1] "double"
> st(expression(aa <- 1))
[1] "expression"
Для структуры вызова вы можете сделать:
st <- function(expr){
str(as.list(match.call()))
}
> st(mean(1:10))
List of 2
$ : symbol st
$ expr: language mean(1:10)
> st(aa <- 1)
List of 2
$ : symbol st
$ expr: language aa <- 1
Комментарии:
1. Неверно говорить
aa <- 1
, что это вычисляется перед вызовомst
. Оно вычисляется только тогда, когда оно ИСПОЛЬЗУЕТСЯ вst
функции. Попробуйтеst <- function(expr) NULL
, а затемst(stop('foo'))
. Выражение stop не используется вst
, поэтому вызов никогда не завершается ошибкой…2. … также обратите внимание, что
typeof(expr)
вычисляетсяexpr
для проверки его типа.
Ответ №3:
Я думаю expr
, что вычисляется перед обработкой функции. Пример POC:
> st <- function(expr){
eval(parse(text=expr))
}
>
> st('aa <- 1')
> aa
Error: object 'aa' not found
Поэтому я думаю, что функция получает expr
только as aa
. Другой пример:
> st <- function(expr){
str(expr)
}
>
> st(aa <- 1)
num 1
Возможно, я ошибаюсь, это скорее интуиция 🙂 Но спасибо, это хорошая головоломка!
Обновить:
> system.time(a <- 1)
user system elapsed
0 0 0
> a
[1] 1
> rm(a)
> fn <- function() a <- 1
> system.time(fn())
user system elapsed
0 0 0
> a
Error: object 'a' not found
Комментарии:
1. В исходном коде для
system.time
вы заметите, чтоexpr
это вычисляется только после выполнения нескольких действий по ведению домашнего хозяйства (например, запуска часов). Ваша модифицированная функция принимает строку в качестве входных данных, а не бит кода (как это делаетsystem.time
).2. @Andrie: это верно, что
expr
можно найти позжеsystem.time
, но это не влияет на основную идею моего ответа. Смотрите обновленные примеры.