Як саме R аналізує `->`, оператор присвоєння права?


76

Отже, це якесь тривіальне питання, але мене бентежить те, що я не можу на нього відповісти, і, можливо, відповідь навчить мене ще кількох деталей про те, як працює R.

Заголовок говорить все: як R аналізує R ->, незрозумілу функцію призначення праворуч?

Мої звичні трюки, щоб зануритися в це, не вдалося:

`->`

Помилка: об’єкт ->не знайдено

getAnywhere("->")

не ->знайдено жодного об’єкта з іменем

І ми не можемо назвати це безпосередньо:

`->`(3,x)

Помилка: не вдалося знайти функцію "->"

Але, звичайно, це працює:

(3 -> x) #assigns the value 3 to the name x
# [1] 3

Здається, Р знає, як просто змінити аргументи, але я думав, що наведені вище підходи напевно розірвали б справу:

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 тим, як він інтерпретується та обробляється; запам'ятовувати та рухатись далі"?


2
На насправді це пов'язано пов'язаний з цим питання набагато більш актуальним: stackoverflow.com/questions/23309687 / ...
MichaelChirico

1
Ви просто встановлюєте значення мітки. Це не означає, що вони однакові
Оле Петерсен

Відповіді:


71

Дозвольте мені вступити до цього, сказавши, що я абсолютно нічого не знаю про те, як працюють парсери. Сказавши це, рядок 296 з gram.y визначає наступні маркери, що представляють призначення в (YACC?) Парсері, який використовує R:

%token      LEFT_ASSIGN EQ_ASSIGN RIGHT_ASSIGN LBB

Потім, у рядках 5140 - 5150 з gram.c , це виглядає як відповідний код С:

Нарешті, починаючи з рядка 5044 з gram.c , визначення install_and_save2:


Тож знову ж таки, маючи нульовий досвід роботи з парсерами, здається, що ->і ->>перекладаються безпосередньо на <-і <<-, відповідно, на дуже низький рівень в процесі інтерпретації.


Ви підняли дуже хороший момент, запитуючи, як синтаксичний аналізатор "знає", щоб змінити аргументи, ->враховуючи те, що, ->здається, встановлено в таблиці символів R як <-- і, отже, мати змогу правильно інтерпретувати x -> yяк y <- xі ні x <- y . Найкраще, що я можу зробити, це надати подальші спекуляції, оскільки я продовжую натрапляти на "докази" на підтвердження своїх тверджень. Сподіваємось, якийсь милосердний експерт YACC спіткнеться щодо цього питання та надасть трохи розуміння; Однак я не збираюся затримувати дихання на цьому.

Повертаючись до рядків 383 та 384 з gram.y , це виглядає як ще деяка логіка синтаксичного аналізу, пов’язана з вищезазначеним LEFT_ASSIGNта RIGHT_ASSIGNсимволами:

|   expr LEFT_ASSIGN expr       { $$ = xxbinary($2,$1,$3);  setId( $$, @$); }
|   expr RIGHT_ASSIGN expr      { $$ = xxbinary($2,$3,$1);  setId( $$, @$); }

Незважаючи на те, що я не можу зробити голови чи хвости цього шаленого синтаксису, я помітив, що другий та третій аргументи xxbinaryзамінюються на WRT LEFT_ASSIGN( xxbinary($2,$1,$3)) та RIGHT_ASSIGN( xxbinary($2,$3,$1)).

Ось, що я уявляю собі в голові:

LEFT_ASSIGN Сценарій: y <- x

  • $2 є другим "аргументом" синтаксичного аналізатора у наведеному вище виразі, тобто <-
  • $1є першим; а самеy
  • $3 є третім; x

Отже, отриманий виклик (C?) Буде xxbinary(<-, y, x).

Застосовуючи цю логіку RIGHT_ASSIGN, тобто x -> y, в поєднанні з моєї попередньої гіпотезою про <-і ->отримувати місця,

  • $2отримує переклад з ->на<-
  • $1 є x
  • $3 є y

Але оскільки результат xxbinary($2,$3,$1)замість xxbinary($2,$1,$3), результат все одно xxbinary(<-, y, x) .


Будівництво від цього трохи далі, ми маємо визначення xxbinaryна лінії 3310 з gram.c :

На жаль, я не зміг знайти правильне визначення lang3(або його варіанти lang1, lang2тощо ...) у вихідному коді R, але я припускаю, що він використовується для оцінки спеціальних функцій (тобто символів) способом, який синхронізується з перекладач.


Оновлення Я спробую відповісти на деякі Ваші додаткові запитання в коментарях, наскільки це можливо, враховуючи мої (дуже) обмежені знання щодо процесу аналізу.

1) Невже це єдиний об’єкт у R, який поводиться так ?? (Я маю на увазі цитату Джона Чемберса через книгу Хедлі: "Все, що існує, є об'єктом. Все, що відбувається, є викликом функції". Це явно лежить поза цим доменом - чи є щось подібне?

По-перше, я погоджуюсь, що це лежить поза цим доменом. Я вважаю, що цитата Chambers стосується середовища R, тобто процесів, що відбуваються після цієї фази розбору низького рівня. Проте я торкнусь цього трохи нижче, однак. У будь-якому випадку, єдиним іншим прикладом такої поведінки, який я міг знайти, є **оператор, який є синонімом більш поширеного оператора степенізації ^. Як і при правильному призначенні, **інтерпретатор, здається, не "розпізнається" як виклик функції тощо ...

R> `->`
#Error: object '->' not found
R> `**`
#Error: object '**' not found 

Я знайшов це, бо це єдиний інший випадок, коли install_and_save2 використовується парсер С :


2) Коли саме це відбувається? Я мав на увазі, що заміна (3 -> y) вже перевернула вираз; Я не міг зрозуміти з джерела, який замінник робить, що спричинило б YACC ...

Звичайно, я все ще тут припускаю, але так, я думаю, ми можемо спокійно припустити, що коли ви телефонуєте substitute(3 -> y), з точки зору функції заміщення , вираз завжди був y <- 3 ; наприклад, функція абсолютно не знає, що ви ввели 3 -> y. do_substitute, як і 99% функцій C, використовуваних R, обробляє лише SEXPаргументи - я вважаю, EXPRSXPу випадку 3 -> y(== y <- 3). Це те, про що я мав на увазі вище, коли я робив різницю між середовищем R та процесом синтаксичного аналізу. Я не думаю, що є щось, що спонукає синтаксичний аналізатор перейти в дію, але все, що ви вводите в інтерпретатор, аналізується. Я зробив трохидокладніше читаючи про генератор парсера YACC / Bison вчора ввечері, і, наскільки я розумію (він же не робить ставку на ферму на цьому), Bison використовує граматику, яку ви визначаєте (у .yфайлі), щоб створити парсер на C тобто функція C, яка виконує фактичний розбір вхідних даних. У свою чергу, все, що ви вводите в сеанс R, спочатку обробляється цією функцією синтаксичного розбору С, яка потім делегує відповідні дії, які потрібно вжити в середовищі R (я, до речі, використовую цей термін дуже вільно). На цьому етапі lhs -> rhsбуде переведено в rhs <- lhs, **в ^тощо ... Наприклад, це витяг з однієї з таблиць примітивних функцій у names.c :

Ви помітите , що ->, ->>і **не визначені тут. Наскільки мені відомо, примітивні вирази R, такі як <-і [, тощо ... є найбільш тісною взаємодією, яку коли-небудь має середовище R з будь-яким кодом, що лежить в основі С. Я припускаю, що на цьому етапі процесу (починаючи з того, що ви вводите в інтерпретатор задані символи та натискаєте клавішу Enter, вгору через фактичну оцінку дійсного виразу R), парсер вже здійснив свою магію, саме тому Ви не можете отримати визначення функції для ->або **оточуючи їх зворотними позначками, як це зазвичай можна.


17
Тим часом наважусь сказати, що ця відповідь варта gram.y? добре, мені слід серйозно повернутися до роботи ...
MichaelChirico

2
Тільки для запису (а також як повноцінного парсера-початківця) зазначу, що, схоже, існує різниця між типом маркера (тут RIGHT_ASSIGN) та його значенням (тут <-, присвоєним yylvalby install_and_save2). Мені здається, що тип звикає керувати синтаксичним розбором виразу (посилаючи нам гілку, яка читає { $$ = xxbinary($2,$3,$1); setId( $$, @$); }), тоді як його значення - це те, що передається через xxbinaryперший аргумент (тобто. $2).
Джош О'Брайен

@Josh O'Brien Дякуємо за вступ (і також редагування); на поверхні, що для мене звучить розумно. Якщо ви готові до цього в якийсь момент, будь ласка, не соромтеся додати ту чи будь-яку іншу відповідну інформацію до моєї відповіді (я боюся, що я б нарізав пояснення, якби я спробував це сформулювати сам).
nrussell

3
@nrussell Ласкаво просимо. lang3та ін. є вбудованими функціями і їх можна знайти тут, у$RHOME/src/include/Rinlinedfuns.h . Мені здається, що їхня роль тут полягає у збитті окремих лексем та проаналізованих виразів у подібні до списку мовні об'єкти, будуючи до повністю проаналізованої версії введеного виразу.
Джош О'Брайен

1
дякую за оновлення! щоб **, я дійсно пам'ятаю , по крайней мере , читання в який - то момент де - то , що цей оператор є свого роду vestigal, так принаймні , я бачив , як це визнається в якості свого роду ізгоєм раніше. так чи інакше, моя побудована утиліта тепер повна сумнівно корисних знань ... як мені це подобається!
MichaelChirico
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.