Які відмінності між операторами присвоєння "=" та "<-" в R?


712

Які відмінності між операторами призначення =і <-в R?

Я знаю, що оператори дещо відрізняються, як показує цей приклад

x <- y <- 5
x = y = 5
x = y <- 5
x <- y = 5
# Error in (x <- y) = 5 : could not find function "<-<-"

Але хіба це єдина різниця?


45
Як зазначалося тут, походження <-символу походить від старих клавіатур APL, які насправді мали на собі одну <-клавішу.
joran

Відповіді:


97

Які відмінності між операторами призначення =і <-в R?

Як показує ваш приклад, =і <-мають дещо інший пріоритет оператора (що визначає порядок оцінки, коли вони змішуються в одному виразі). Фактично, ?Syntaxв R подається наступна таблиця пріоритетності оператора, від найвищої до найнижчої:

…
‘-> ->>’           rightwards assignment
‘<- <<-’           assignment (right to left)=’                assignment (right to left)

Але хіба це єдина різниця?

Оскільки ви запитували про операторів призначення : так, це єдина різниця. Однак вам би пробачили, що вірите в інше. Навіть документація R ?assignOpsстверджує, що існує більше відмінностей:

Оператор <-може бути використаний будь-де, тоді як оператор =дозволений лише на верхньому рівні (наприклад, у повному виразі, набраному в командному рядку) або в якості одного з піддепресій у скопленому списку виразів.

Не будемо ставити на нього занадто точну крапку: документація на R (тонко) неправильна [ 1 ] . Це легко показати: нам просто потрібно знайти зустрічний приклад =оператора, який не є (а) на верхньому рівні, а також (b) суб-вираження у скопленому списку виразів (тобто {…; …}). - Без зайвого галасу:

x
# Error: object 'x' not found
sum((x = 1), 2)
# [1] 3
x
# [1] 1

Ясно, що ми виконали завдання, використовуючи =поза контекстами (a) та (b). Отже, чому документація основної функції мови R протягом десятиліть помилялася?

Це тому, що в синтаксисі R символ =має два різних значення, які регулярно плутаються:

  1. Перше значення - як оператор присвоєння . Це все, про що ми говорили поки що.
  2. Другий сенс - це не оператор, а швидше синтаксичний маркер, який сигналізує з назвою аргументу, що передається у виклику функції. На відміну від =оператора, він не виконує жодних дій під час виконання, він просто змінює спосіб аналізу вираження.

Подивимось.

У будь-якому фрагменті коду загальної форми ...

‹function_name›(‹argname› = ‹value›,)
‹function_name›(‹args›, ‹argname› = ‹value›,)

=Знак - це маркер, який визначає названий аргумент, що передається: це не оператор присвоєння. Крім того, =цілком заборонено в деяких синтаксичних контекстах:

if (‹var› = ‹value›) …
while (‹var› = ‹value›) …
for (‹var› = ‹value› in ‹value2›) …
for (‹var1› in ‹var2› = ‹value›) …

Будь-яке з них призведе до помилки "несподівано" = "у ‹bla›".

У будь-якому іншому контексті =відноситься до призначення виклику оператора. Зокрема, лише встановлення круглих дужок навколо підвираження робить будь-яке з перерахованих вище пунктів (а) дійсним та (b) призначенням . Наприклад, наступне виконує завдання:

median((x = 1 : 10))

Але також:

if (! (nf = length(from))) return()

Тепер ви можете заперечити, що такий код жорстокий (і ви можете мати рацію). Але я взяв цей код із base::file.copyфункції (замінивши<- з =) - це всеосяжний стиль в більшій частині ядра R кодову.

Оригінальне пояснення Джона Chambers , яким документація по R, ймовірно , на основі, на самому ділі пояснює це правильно:

[ =присвоєння] дозволено лише у двох місцях граматики: на верхньому рівні (як повноцінна програма або вираз, введений користувачем); і коли вони ізольовані від оточуючої логічної структури, дужками або додатковою парою дужок.


Зізнання: я брехав раніше. Там є одна додаткова різниця між =і <-операторами: вони називають різні функції. За замовчуванням ці функції роблять те саме, але ви можете змінити будь-яку з них окремо, щоб змінити поведінку. Навпаки, <-і ->(призначення зліва направо), хоч і синтаксично виразні, завжди викликають одну і ту ж функцію. Переосмислення одного також перекриває інше. Знаючи це, рідко буває практично, але це може бути використано для веселих шенагіганів .


1
Про пріоритет, і помилки в доці лепехи, то старшинство ?фактично прямо між =і <-, що має важливі наслідки при перевизначенні ? , і практично ні в іншому випадку.
Moody_Mudskipper

@Moody_Mudskipper це химерно! Ви, здається, маєте рацію, але згідно з вихідним кодом ( main/gram.y), пріоритет ?правильно задокументований та нижчий за обидва =та <-.
Конрад Рудольф

Я не розмовляю зі С, але я вважаю, що =отримають спеціальну обробку до того, як буде побудовано дерево розбору. Можливо, пов'язане з аргументами функції, має сенс, що foo(x = a ? b)ми повинні шукати, =перш ніж розбирати решту виразу.
Moody_Mudskipper


2
@Moody_Mudskipper FWIW це остаточно зафіксовано в 4.0.0.
Конрад Рудольф

661

Різниця в операторах присвоєння чіткіша, коли ви використовуєте їх для встановлення значення аргументу у виклику функції. Наприклад:

median(x = 1:10)
x   
## Error: object 'x' not found

У цьому випадку xоголошується в межах функції, тому її немає в робочій області користувача.

median(x <- 1:10)
x    
## [1]  1  2  3  4  5  6  7  8  9 10

У цьому випадку xоголошується в робочій області користувача, тому ви можете використовувати його після завершення виклику функції.


Серед спільноти R є загальна перевага щодо використання <-для присвоєння (крім функцій підписів) щодо сумісності із (дуже) старими версіями S-Plus. Зауважте, що пробіли допомагають з’ясувати подібні ситуації

x<-3
# Does this mean assignment?
x <- 3
# Or less than?
x < -3

Більшість R IDE мають комбінації клавіш, щоб <-полегшити їх введення. Ctrl+ =в архітекторі, Alt+ -в RStudio ( Option+ -під macOS), Shift+ -(підкреслення) у emacs + ESS.


Якщо ви віддаєте перевагу писати =на <-але хочете використовувати більш загальний символ присвоювання для публічно випущеної коди (на CRAN, наприклад), то ви можете використовувати одну з tidy_*функцій в formatRпакеті для автоматичної заміни =з <-.

library(formatR)
tidy_source(text = "x=1:5", arrow = TRUE)
## x <- 1:5

Відповідь на питання "Чому x <- y = 5кидає помилку, але ні x <- y <- 5?" це "Це до магії, що міститься в аналізаторі". Синтаксис R містить безліч неоднозначних випадків , які потрібно вирішити так чи інакше. Аналізатор вибирає розв'язувати біти виразу в різних порядках залежно від того, використовувались =або <-використовувались.

Щоб зрозуміти, що відбувається, потрібно знати, що присвоєння мовчки повертає значення, яке було призначено. Це можна зрозуміти, наприклад, чітко надрукувавши print(x <- 2 + 3).

По-друге, зрозуміліше, якщо ми будемо використовувати позначення префікса для призначення. Тому

x <- 5
`<-`(x, 5)  #same thing

y = 5
`=`(y, 5)   #also the same thing

Парсер інтерпретує x <- y <- 5як

`<-`(x, `<-`(y, 5))

Ми можемо очікувати, що x <- y = 5тоді це буде

`<-`(x, `=`(y, 5))

але насправді це трактується як

`=`(`<-`(x, y), 5)

Це тому =, що пріоритет нижчий, ніж <-показано на ?Syntaxдовідковій сторінці.


4
Про це також згадується у розділі 8.2.26 «Інферно » Патріка Бернса (не я, а рекомендація все одно)
Уве

3
Однак median((x = 1:10))має той же ефект, що і median(x <- 1:10).
Франческо Наполітано

2
я не вважаю їх ярликами, і в будь-якому випадку ти натискаєш однакову кількість клавіш
yosemite_k

5
Я щойно зрозумів, що ваше пояснення того, як x <- x = 5трактується, злегка неправильне: насправді R трактує це як ​`<-<-`(x, y = 5, value = 5)(що саме по собі є більш-менш рівнозначним tmp <- x; x <- `<-<-`(tmp, y = 5, value = 5)). Yikes!
Конрад Рудольф

4
… І я щойно зрозумів, що сама перша частина цієї відповіді є невірною і, на жаль, досить оманливою, оскільки вона утримує поширене неправильне уявлення: спосіб, який ви використовуєте =у виклику функції , не виконує призначення та не є оператором присвоєння. Це абсолютно чіткий синтаксичний синтаксичний вираз R, який, як правило, використовує той самий символ. Крім того, показаний вами код не "декларує" xв межах функції. Оголошення функції виконує згадане оголошення. Виклик функції не робить (стає трохи складніше з названими ...аргументами).
Конрад Рудольф

103

Посібник зі стилю R спрощує проблему, забороняючи призначати "=". Непоганий вибір.

https://google.github.io/styleguide/Rguide.xml

Посібник з R детально описується на всіх 5 операторах призначення.

http://stat.ethz.ch/R-manual/R-patched/library/base/html/assignOps.html


133
Мінус випадкового присвоєння до того часу, x<-yколи це x < -yбуло призначено, так сильно розбурхує мене, що я особисто віддаю перевагу =. Наявність вашого коду залежно від присутності білого простору мені не здається гарним. Добре пропонувати інтервали як стильові поради, але щоб ваш код працював інакше, чи є пробіл чи ні? Що робити, якщо ви переформатуєте код або використовуєте пошук і заміну, пробіли іноді можуть зникати, а код зникає. З цим не проблема =. IIUC, забороняючи =прирівнюється до вимоги " <- "; тобто 3 символи, включаючи пробіл, а не лише " <-".
Метт Даул

12
Зауважте, що будь-який не-0 розглядається TRUEР. Отже, якщо ви маєте намір перевірити, чи xне менше -y, ви можете написати, if (x<-y)що не попередить або помилиться, і, здається, спрацює нормально. Це буде лише FALSEколи y=0.
Метт Даул

4
Якщо ви забороняєте =та використовуєте, <- то важко стверджувати, що зайвий крок grep "[^<]<-[^ ]" *.Rне потрібен. =не потребує такого grep.
Метт Даул

34
Навіщо боліти очі і палець, <-якщо можете використовувати =? У 99,99% разів =це добре. Іноді вам потрібно, <<-але це вже інша історія.
Фернандо

10
Зосередженість на <-, можливо, одна з кульгавих причин відсутності + = і - =.
Кріс

37

x = y = 5еквівалентно x = (y = 5)тому, що оператори присвоєння "групують" праворуч ліворуч, що працює. Значення: призначити 5 y, залишити число 5; а потім призначте ці 5 x.

Це не те саме (x = y) = 5, що не працює! Значення: присвоїти значення , yщоб x, в результаті чого значенняy ; а потім призначити 5, гмм ..., що саме?

Коли ви змішуєте різні види операторів присвоєння, <-пов'язує міцніше, ніж =. Так x = y <- 5трактується як x = (y <- 5), що є випадком, який має сенс.

На жаль, x <- y = 5трактується як(x <- y) = 5 , що є випадком, який не працює!

Дивіться ?Syntaxі ?assignOpsправила пріоритету (обов'язковість) та групування.


Так, як сказано у відповіді Конрада Рудольфа<- <<- вище = в таблиці пріоритетів, це означає, що <-буде виконано першим. Отже, x <- y = 5слід виконати як (x <- y) = 5.
Нік Донг

1
@Nick Dong Так. Корисно, що таблиця преференцій оператора однозначно задокументована у «Синтаксисі {base} .
Стів

33

За словами Джона Чемберса, оператора =дозволено лише на "найвищому рівні", а це означає, що він не допускається в таких структурах управління if, що робить такі помилки програмування незаконними.

> if(x = 0) 1 else x
Error: syntax error

Як він пише, "заборона нової форми присвоєння [=] у виразах управління дозволяє уникнути помилок програмування (наприклад, наведений вище приклад), які швидше мають рівний оператор, ніж інші призначення S".

Ви можете це зробити, якщо це "ізольовано від оточуючої логічної структури, дужками або додатковою парою дужок", так if ((x = 0)) 1 else xби спрацювало.

Див. Http://developer.r-project.org/equalAssign.html


11
Це звичайна помилка, x==0майже завжди мається на увазі замість неї.
Аарон залишив стек переповнення

14
Ага, так, я не помітив, що ви сказали "помилка програмування". Насправді хороша новина, що це викликає помилку. І вагомий привід віддати перевагу x=0завданню над x<-0!
Стів Пітчерс

7
Так, приємно, що це викликає помилку, хоча я малюю інший урок про те, що віддати перевагу; Я вибираю використовувати =якомога менше , тому =і ==виглядають так схожі.
Аарон залишив стек переповнення

2
Те, як подано цей приклад, для мене таке дивне. if(x = 0) 1 else xвидає помилку, допомагаючи мені знайти та виправити помилку. if(x <- 1) 1 else xне кидає помилки і дуже заплутано.
Грегор Томас

3
Я маю на увазі, справді корисна перевірка помилок кинула б там помилку і сказала «у вас є марний код, який завжди повертає elseзначення, ви мали на увазі записати це так?», Але, це може бути мрія труби ...
TylerH

26

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


8
Я думаю, що "верхній рівень" означає на рівні висловлювання, а не на рівні вираження. Отже, x <- 42сама по собі є твердженням; в if (x <- 42) {}ньому було б виразом, але недійсним. Щоб було зрозуміло, це не має нічого спільного з тим, перебуваєте ви в глобальному середовищі чи ні.
Стів Пітчерс

1
Це: "оператор = дозволено лише на найвищому рівні" - це поширене непорозуміння і абсолютно неправильне.
Конрад Рудольф

Це неправда - наприклад, це працює, навіть якщо призначення не є повним виразом:1 + (x = 2)
Павло Мінаєв

1
Щоб уточнити коментарі КонрадРудольфа та ПавлаМінаєва, я вважаю, що це занадто сильно, щоб сказати, що це абсолютно неправильно, але є виняток, який є, коли він "ізольований від оточуючої логічної структури, дужками або додатковою парою дужок".
Аарон вийшов із переповнення стека

Або function() x = 1, repeat x = 1, if (TRUE) x = 1....
Moody_Mudskipper

6

Це також може додати розуміння різниці між цими двома операторами:

df <- data.frame(
      a = rnorm(10),
      b <- rnorm(10)
)

Для першого елемента R присвоїли значення та власне ім'я, тоді як ім'я другого елемента виглядає дещо дивним.

str(df)
# 'data.frame': 10 obs. of  2 variables:
#  $ a             : num  0.6393 1.125 -1.2514 0.0729 -1.3292 ...
#  $ b....rnorm.10.: num  0.2485 0.0391 -1.6532 -0.3366 1.1951 ...

R версія 3.3.2 (2016-10-31); macOS Sierra 10.12.1


6
чи можете ви дати більш детальне пояснення, чому це відбувається / що відбувається тут? (підказка: data.frameнамагається використовувати ім’я наданої змінної як назву елемента в кадрі даних)
Бен Болкер

Подумав лише, чи може це бути помилка? І якщо так, то як і де я звітую про це?
Денис Расулев

7
це не помилка. Я намагався натякнути на відповідь у своєму коментарі вище. Під час встановлення назви елемента R буде використовувати еквівалент make.names("b <- rnorm(10)").
Бен Болкер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.