Что именно в R вызывает оценку объекта имени типа (или символа)?

После запуска:

x <- as.name("aa")
aa <- 2

в R, почему нет

(x)

вернуть 2? И почему не

x <- as.name("aa")
aa <- 3
get(get(x))

вернуть 3?

Я знаю, что get() ожидает строку, но я не понимаю, почему она не оценивает x, не находит строку внутри и не получает ее. Мне кажется, что иногда функции делают такую ​​оценку своих аргументов, а иногда нет. Например, во втором примере, если вы замените get(get(x)) на eval(x), eval() оценивает x, чтобы найти имя, а затем вычисляет имя, чтобы найти 3.


person andrewH    schedule 06.05.2016    source источник
comment
Я сбит с толку вашим редактированием. Вы, кажется, спрашиваете, почему разработчики R не заставили get делать то, что делает eval, но тогда ответ должен был бы быть, потому что они делают две разные вещи: одна вычисляет выражение (которое может быть символом), а другая извлекает объекты где вы указываете имя (символ) объекта через символ. Почему бы не разделить разные функции на две разные функции?   -  person joran    schedule 07.05.2016


Ответы (2)


Я думаю, что ответ @joran правильный, но, может быть, я могу попытаться объяснить по-другому.

«Функция» ( в R по сути является функцией тождества. Он отражает то, что вы передаете ему. Его почти как нет. Нет никакой разницы между тем, что будет возвращено этими операторами

x      #1
(x)    #2
((x))  #3

Скобка просто передает значение внутри. Вы можете добавить столько круглых скобок, сколько хотите, и это не изменит того, что возвращается. Оценщик смотрит на ((x)), видит внешнюю скобку и знает, что нужно просто вернуть значение вещи внутри скобки. Итак, теперь он анализирует только (x) и снова видит внешнюю скобку и просто возвращает значение внутри скобки, то есть x. Скобка просто передает значение изнутри; они не оценивают это.

Голое значение x — это имя (или символ). Имя не связано однозначно со значением. Сопоставление между именами и значениями зависит от среды. Вот почему имена должны оцениваться в определенном контексте, чтобы получить значение. Рассмотрим эти примеры

aa <- 5
dd <- data.frame(aa=20)
x <- as.name("aa")
foo <- function(x) {aa<-10; eval(x)}

eval(x)
# [1] 5
foo(x)
# [1] 10
eval(x, dd)
# [1] 20

Такое поведение на самом деле очень желательно. Это то, что заставляет функции, требующие нестандартной оценки, например

subset(mtcars, hp<100)

Когда вы используете консоль R, она ведет себя как REPL -- он читает ваш ввод, оценивает его, печатает, а затем ожидает следующего ввода. Обратите внимание, что он выполняет только один уровень оценки, и оценка происходит в «текущей» среде. Он не оценивает рекурсивно возвращаемое значение из выражения. Итак, когда вы делаете

x <- as.name("aa")
x   # identical to (x)
# aa

когда REPL доходит до шага оценки, он оценивает имя x, которое указывает на имя aa. Вот и все. Один уровень оценки. Имя aa впоследствии не оценивается.

На странице справки ?eval есть примечание:

eval оценивает свой первый аргумент в текущей области перед тем, как передать его оценщику

Там не происходит «двойной» оценки. Он просто оценивает свои параметры, как и любая другая функция в R. Например

aa <- 5 
bar <- function(x) print(x)
bar(aa+2)
# [1] 7

Он печатает «7», а не «аа+2», потому что функция оценила свой параметр перед печатью. Это также объясняет различия между этими двумя

dd <- data.frame(bb=20)
xx <- as.name("bb")
eval(bb, dd)
# Error in eval(bb, dd) : object 'bb' not found
eval(xx, dd)
# [1] 20

При первом вызове eval() R не может оценить bb в текущей среде, поэтому вы получаете ошибку. Но обратите внимание, что

evalq(bb, dd)

работает, потому что evalq не пытается вычислить первый параметр выражения.

person MrFlick    schedule 17.05.2016
comment
Уважаемый @MrFlick Это очень полезно. Но я все еще не понимаю, почему дано: x ‹- as.name(aa); аа ‹- 2; если (x) возвращает то же самое, что и x в командной строке (что и делает: символ aa), почему ((x)) не возвращает 2? (x) возвращает aa в командной строке, а aa возвращает 2. - person andrewH; 04.06.2016
comment
Потому что здесь происходит только один возврат. Строка оценивается ровно один раз. Двойной оценки нет. Скобки не заставляют оценивать. - person MrFlick; 04.06.2016
comment
Хорошо, может быть, я понял. Скобки на самом деле ничего не делают, кроме группировки. В частности, они не делают то же самое, что и ввод x в консоли R. Это командная строка консоли R, которая, как вы выразились, ведет себя как цикл чтение-оценка-печать, а не функция (. Итак, ((x)) поступает в командную строку как x, а не как aa. Он не становится aa до тех пор, пока к нему не попадет командная строка, и именно консольная командная строка преобразует x в aa. Добавление дополнительных слоев скобок не имеет значения, потому что даже самые внутренние скобки ничего не делают с x. Это правильно? - person andrewH; 04.06.2016
comment
Кстати, эта строка: eval оценивает свой первый аргумент в текущей области видимости, прежде чем передать его оценщику, который я, должно быть, прочитал 20 раз, не вникая в его смысл, объясняет каждую неудачу в предсказании того, что eval() сделает, которую я когда-либо имел. За исключением тех, которые не вызваны eval. Большое двойное спасибо от меня. - person andrewH; 04.06.2016
comment
Да, я думаю, вы получили это сейчас! - person MrFlick; 04.06.2016

Поскольку значение x не равно 2, это символ (или имя) aa. Однако, если вы eval это:

> eval(x)
[1] 2

Точно так же get(x) вообще не работает (то есть выдает ошибку), потому что, согласно документации для get, его первым аргументом должно быть an object name (given as a character string), где скобки предназначены для того, чтобы отличить его от символа/имени.

get работает только с символьным аргументом:

 > get("aa")
[1] 2

И symbol (которое я нахожу менее запутанным, чем name) — это не одно и то же:

> identical("aa",as.name("aa"))
[1] FALSE

(as.name и as.symbol делают то же самое.)

Отличное объяснение различия между «оценкой выражений» и «оценкой аргументов функции», о котором я упоминаю ниже в комментарии, см. в ответе @MrFlick.

person joran    schedule 06.05.2016
comment
Я думаю, что я по-прежнему не понимаю, почему R ведет себя иначе, если я набираю символ, чем если функция возвращает тот же символ. eval(x) не возвращает символ — он вычисляет выражение x, находит символ, а затем вычисляет символ, чтобы найти значение. Оценка происходит дважды. Если я наберу (aa), R вернет 2. если я наберу x, R вернет aa, символ типа. Но если я наберу (x), R все равно вернет символ. Я не понимаю, почему после того, как внутренние скобки оцениваются как символ, внешние скобки не возвращают то, что они делают, если я набираю тот же символ в скобках. - person andrewH; 17.05.2016
comment
Мой пример get() был плохо сконструирован. Позвольте мне повторить. Если я наберу (get(x)), как и выше, он снова вернет символ. С другой стороны, get(get(x)) возвращает ошибку. Он не оценивает get(x) как символ, не заключает его в кавычки, а затем не вычисляет символ в кавычках. Это имеет для меня больше смысла, но только потому, что я не ожидаю, что кавычки будут вести себя как обычная функция, оценивая ее аргумент и затем возвращая результат в виде строки. Но в (get(x)) я ожидаю, что аргумент внешней скобки будет оценен, а это не так. - person andrewH; 17.05.2016
comment
@andrewH Я думаю, вы путаете оценку произвольного выражения R в определенной среде с оценкой аргумента функции, где последний действительно означает нечто более близкое к привязке обещания к значению. () ведет себя семантически так же, как функция identity, поэтому любое вычисление ее аргументов просто означает, что обещание преобразуется в значение. Рассмотрим identity(x = get("x")). Оценивается аргумент x, а результатом является его привязка к значению, возвращаемому get("x"). - person joran; 17.05.2016