Как именно R анализирует` ->', оператор присваивания справа?
так что это своего рода тривиальный вопрос, но меня беспокоит, что я не могу на него ответить, и, возможно, ответ научит меня еще некоторым деталям о том, как работает R.
название говорит само за себя: как R разбирает ->
, неясная функция назначения правой стороны?
мои обычные трюки, чтобы погрузиться в это не удалось:
`->`
ошибка: объект
->
не нашел
getAnywhere("->")
нет объекта с именем
->
был нашел
и мы не можем назвать его напрямую:
`->`(3,x)
ошибка: не удалось найти функцию
"->"
но конечно, это работает:
(3 -> x) #assigns the value 3 to the name x
# [1] 3
похоже, R знает, как просто отменить аргументы, но я думал, что вышеупомянутые подходы, несомненно, взломали бы дело:
pryr::ast(3 -> y)
# - ()
# - `<- #R interpreter clearly flipped things around
# - `y # (by the time it gets to `ast`, at least...)
# - 3 # (note: this is because `substitute(3 -> y)`
# # already returns the reversed version)
сравните это с обычным оператором присваивания:
`<-`
.Primitive("<-")
`<-`(x, 3) #assigns the value 3 to the name x, as expected
?"->"
,?assignOps
, и Определение Языка R все просто упоминают его попутно как правильный оператор присваивания.
но там явно что-то уникальное о том, как это. Это не функция / оператор (как вызовы getAnywhere
и непосредственно `->`
кажется, чтобы продемонстрировать), так что же это? Это полностью в своем собственном классе?
есть ли что-нибудь, чтобы избавиться от этого, кроме "->
полностью уникален в языке R в том, как он интерпретируется и обработано; запомнить и двигаться дальше"?
1 ответ:
позвольте мне предварить это, сказав, что я абсолютно ничего не знаю о том, как работают Парсеры. Сказав это,линия 296 грамм.г определяет следующие маркеры для представления назначения в (YACC?) анализатор R использует:
%token LEFT_ASSIGN EQ_ASSIGN RIGHT_ASSIGN LBB
затем, на линиях 5140 через 5150 грамм.c, это выглядит как соответствующий код C:
case '-': if (nextchar('>')) { if (nextchar('>')) { yylval = install_and_save2("<<-", "->>"); return RIGHT_ASSIGN; } else { yylval = install_and_save2("<-", "->"); return RIGHT_ASSIGN; } }
наконец, начиная с линия 5044 грамма.c определение
install_and_save2
:/* Get an R symbol, and set different yytext. Used for translation of -> to <-. ->> to <<- */ static SEXP install_and_save2(char * text, char * savetext) { strcpy(yytext, savetext); return install(text); }
так опять же, имея нулевой опыт работы с парсерами, кажется, что
->
и->>
переводятся непосредственно в<-
и<<-
, соответственно, при a очень низкий уровень в процессе интерпретации.
вы подняли очень хороший вопрос, спрашивая, как парсер "знает", чтобы изменить аргументы на
->
- учитывая, что->
кажется, что он установлен в таблицу символов R как<-
- и при этом уметь правильно интерпретироватьx -> y
какy <- x
и неx <- y
. Лучшее, что я могу сделать, это предоставить дальнейшие предположения, поскольку я продолжаю сталкиваться с "доказательствами" в поддержку моих утверждений. Надеюсь, какой-нибудь милосердный эксперт YACC наткнется на этот вопрос и предоставит немного информации; я не собираюсь задерживать дыхание на этом.на линии 383 и 384 грамма.г, это выглядит как еще одна логика разбора, связанная с вышеупомянутым
LEFT_ASSIGN
иRIGHT_ASSIGN
символы:| expr LEFT_ASSIGN expr { $$ = xxbinary(,,); setId( $$, @$); } | expr RIGHT_ASSIGN expr { $$ = xxbinary(,,); setId( $$, @$); }
хотя я не могу действительно сделать головы или хвосты этого сумасшедшего синтаксиса, я заметил, что второй и третий аргументы
xxbinary
заменяются на WRTLEFT_ASSIGN
(xxbinary(,,)
) иRIGHT_ASSIGN
(xxbinary(,,)
).вот что я представляю в голове:
LEFT_ASSIGN
сценарий:y <- x
является вторым "аргументом" для парсера в приведенном выше выражении, т. е.
<-
это первое; а именно
y
- третий;
x
таким образом, в результате (C?) вызов будет
xxbinary(<-, y, x)
.применяя эту логику к
RIGHT_ASSIGN
, т. е.x -> y
в сочетании с моей предыдущей гипотезы о<-
и->
начало поменялись,
переводится с
->
до<-
is
x
и
y
но так как результат
xxbinary(,,)
вместоxxbinary(,,)
, результат ещеxxbinary(<-, y, x)
.
отталкиваясь от этого немного дальше, у нас есть определение
xxbinary
on линия 3310 грамм.c:static SEXP xxbinary(SEXP n1, SEXP n2, SEXP n3) { SEXP ans; if (GenerateCode) PROTECT(ans = lang3(n1, n2, n3)); else PROTECT(ans = R_NilValue); UNPROTECT_PTR(n2); UNPROTECT_PTR(n3); return ans; }
к сожалению, я не смог найти правильное определение
lang3
(или его вариантыlang1
,lang2
и т. д...) в источнике R код, но я предполагаю, что он используется для оценки специальных функций (т. е. символов) таким образом, что синхронизируется с интерпретатором.
обновления Я постараюсь ответить на некоторые из ваших дополнительных вопросов в комментариях, насколько это возможно, учитывая мои (очень) ограниченные знания о процессе разбора.
1) это действительно единственный объект в R, который ведет себя так?? (У меня есть я вспомнил цитату Джона Чамберса через Хэдли книга: "все то, что существует-это объект. Все, что происходит-это вызов функции." Это явно лежит за пределами этой области - есть ли что-нибудь еще, как это?
во-первых, я согласен, что это лежит за пределами этого домена. Я считаю, что цитата Чемберса касается среды R, т. е. процессов, которые происходят после этой фазы анализа низкого уровня. Однако я коснусь этого немного ниже. В любом случае, единственный другой пример такого поведения я можно найти
**
оператор, который является синонимом более распространенного оператора возведения в степень^
. Как и при правильном назначении,**
кажется, не" распознается " как вызов функции и т. д... переводчиком:R> `->` #Error: object '->' not found R> `**` #Error: object '**' not found
я нашел это, потому что это единственный случай, когда
install_and_save2
используется синтаксическим анализатором C:case '*': /* Replace ** by ^. This has been here since 1998, but is undocumented (at least in the obvious places). It is in the index of the Blue Book with a reference to p. 431, the help for 'Deprecated'. S-PLUS 6.2 still allowed this, so presumably it was for compatibility with S. */ if (nextchar('*')) { yylval = install_and_save2("^", "**"); return '^'; } else yylval = install_and_save("*"); return c;
2) когда именно это происходит? Я имею в виду, что замена(3 -> г) имеет я уже перевернул выражение; я не мог понять из источника, какой заменитель делает это, чтобы пинговать YACC...
конечно, я все еще размышляю здесь, Но да, я думаю, что мы можем смело предположить, что когда вы звоните
substitute(3 -> y)
с точки зрения замена функция, выражение всегдаy <- 3
; например, функция полностью не знает, что вы набрали3 -> y
.do_substitute
, как и 99% функций C используется R, только ручкиSEXP
аргументы - этоEXPRSXP
в случае3 -> y
( = =y <- 3
), я верю. Это то, что я имел в виду выше, когда я сделал различие между средой R и процессом разбора. Я не думаю, что есть что - то, что специально запускает парсер, чтобы перейти в действие-но скорее все вы вводите в переводчик разбирается. Я сделал мало подробнее о парсере YACC / Bison генератор вчера вечером, и как я понимаю (ака не ставьте ферму на это), Зубр использует грамматику, которую вы определяете (в.y
file (s)) to создать парсер в C-т. е. функция C, которая выполняет фактический разбор ввода. В свою очередь, все, что вы вводите в сеансе R, сначала обрабатывается этой функцией синтаксического анализа C, которая затем делегирует соответствующее действие, которое должно быть выполнено в среде R (кстати, я использую этот термин очень свободно). Во время этого фаза,lhs -> rhs
будет переведен наrhs <- lhs
,**
до^
и т. д... Например, это отрывок из одного из таблицы примитивных функций в названиях.c:/* Language Related Constructs */ /* Primitives */ {"if", do_if, 0, 200, -1, {PP_IF, PREC_FN, 1}}, {"while", do_while, 0, 100, 2, {PP_WHILE, PREC_FN, 0}}, {"for", do_for, 0, 100, 3, {PP_FOR, PREC_FN, 0}}, {"repeat", do_repeat, 0, 100, 1, {PP_REPEAT, PREC_FN, 0}}, {"break", do_break, CTXT_BREAK, 0, 0, {PP_BREAK, PREC_FN, 0}}, {"next", do_break, CTXT_NEXT, 0, 0, {PP_NEXT, PREC_FN, 0}}, {"return", do_return, 0, 0, -1, {PP_RETURN, PREC_FN, 0}}, {"function", do_function, 0, 0, -1, {PP_FUNCTION,PREC_FN, 0}}, {"<-", do_set, 1, 100, -1, {PP_ASSIGN, PREC_LEFT, 1}}, {"=", do_set, 3, 100, -1, {PP_ASSIGN, PREC_EQ, 1}}, {"<<-", do_set, 2, 100, -1, {PP_ASSIGN2, PREC_LEFT, 1}}, {"{", do_begin, 0, 200, -1, {PP_CURLY, PREC_FN, 0}}, {"(", do_paren, 0, 1, 1, {PP_PAREN, PREC_FN, 0}},
Вы заметите, что
->
,->>
и**
здесь не определены. Насколько я знаю, R примитивные выражения, такие как<-
и[
и т. д... это самое тесное взаимодействие среды R, либо с какого-либо базового кода. Я предлагаю, что на этом этапе процесса (от ввода набора символов в интерпретатор и нажатия "Enter" до фактической оценки допустимого выражения R) парсер уже сработал свою магию, поэтому вы не можете получить определение функции для->
или**
окружая их с помощью задних палочек, как вы обычно можете.