Каковы различия между «=» и «<-» в R?

514

Каковы различия между операторами присваивания = и <- в R?

Я знаю, что операторы немного отличаются, как показывает этот пример

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

Но разве это единственное различие?

  • 30
    Как уже отмечалось здесь истоки <- символ пришел из старых клавиатур APL , которые фактически имели один <- ключ на них.
Теги:
assignment-operator
r-faq

7 ответов

499
Лучший ответ

Разница в операторах назначения более понятна, когда вы используете их для установки значения аргумента в вызове функции. Например:

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.

  • 3
    Это также упоминается в главе 8.2.26 « The Inferno » Патрика Бернса (в любом случае, не я, а рекомендация)
  • 2
    Тем не менее, median((x = 1:10)) имеет тот же эффект, что и median(x <- 1:10) .
Показать ещё 4 комментария
83

Руководство по стилю Google R упрощает эту проблему, запрещая присвоение "=". Неплохой выбор.

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

В руководстве R содержатся подробные сведения обо всех 5 операторах присваивания.

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

  • 115
    Недостаток случайного назначения x<-y когда имелось в виду x < -y , огорчает меня настолько, что я лично предпочитаю = . Наличие вашего кода зависит от наличия пробелов, мне не кажется хорошим. Можно рекомендовать интервалы в качестве рекомендаций по стилю, но для того, чтобы ваш код работал по-разному, есть ли пробел или нет? Что, если вы переформатируете свой код или воспользуетесь поиском и заменой, пробельные символы могут иногда исчезать и код будет ошибочным. Это не проблема с = . IIUC, запрещающий = соответствует требованию " <- "; т.е. 3 символа, включая пробел, а не просто " <- ".
  • 12
    Обратите внимание, что любой не-0 считается TRUE R. Поэтому, если вы намереваетесь проверить, если x меньше -y , вы можете написать if (x<-y) который не будет предупреждать или выдавать ошибку, и, кажется, работает нормально. Это будет только FALSE когда y=0 , хотя.
Показать ещё 6 комментариев
28

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

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

Как он пишет: "Запрещение новой формы присваивания [=] в контрольных выражениях позволяет избежать ошибок программирования (например, вышеприведенного примера), которые более вероятны с равным оператором, чем с другими S-назначениями".

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

См. http://developer.r-project.org/equalAssign.html

  • 0
    Что вы ожидаете от этой линии? Конечно, это эквивалентно x = 0 ?
  • 11
    Это распространенная ошибка, вместо этого почти всегда подразумевается x==0 .
Показать ещё 7 комментариев
26

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 для ?assignOps приоритета (привязки) и группировки.

20

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

  • 8
    Я думаю, что «верхний уровень» означает на уровне выражения, а не на уровне выражения. Таким образом, x <- 42 по себе является утверждением; в if (x <- 42) {} это будет выражение и недопустимо. Чтобы было ясно, это не имеет никакого отношения к тому, находитесь ли вы в глобальной среде или нет.
  • 0
    Это: «оператор = разрешен только на верхнем уровне» является широко распространенным недоразумением и совершенно ошибочным.
Показать ещё 1 комментарий
12

Каковы различия между операторами присваивания = и <- в R?

Как показывает ваш пример, = и <- имеют немного отличающийся приоритет оператора (который определяет порядок оценки, когда они смешиваются в одном выражении). На самом деле ?Syntax в R дает следующую таблицу приоритета оператора: от самого высокого до самого низкого:

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

Но разве это единственное различие?

Поскольку вы спрашивали об операторах присваивания: да, это единственная разница. Однако вам будет отказано в вере. Даже R-документация ?assignOps утверждает, что есть больше различий:

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

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

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

Очевидно, что мы выполнили присваивание, используя =, вне контекстов (a) и (b). Итак, почему документация о ключевой языковой функции R была неправильной на протяжении десятилетий?

Это связано с тем, что в синтаксисе Rs символ = имеет два разных значения, которые обычно объединяются:

  1. Первое значение - это оператор присваивания. Об этом до сих пор говорили все.
  2. Второе значение не является оператором, а скорее синтаксическим токеном, который передает именованный аргумент, передаваемый в вызове функции. В отличие от оператора = он не выполняет никаких действий во время выполнения, он просто изменяет способ анализа выражения.

Посмотрим.

В любом фрагменте кода общей формы...

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

... the = - это токен, который определяет передачу именованных аргументов: это не оператор присваивания. Кроме того, = совершенно запрещено в некоторых синтаксических контекстах:

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

Любой из них вызовет ошибку "неожиданно" = "в <bla>".

В любом другом контексте = относится к вызову оператора присваивания. В частности, просто размещение круглых скобок вокруг подвыражения делает любое из вышеуказанных (а) действительным и (б) назначением. Например, следующее выполняет назначение:

median((x = 1 : 10))

Но также:

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

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

Первоначальное объяснение Джона Чамберса, на котором, вероятно, основано на R-документации, на самом деле объясняет это правильно:

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


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

4

Это также может добавить к пониманию разницы между этими двумя операторами:

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

  • 5
    Можете ли вы дать более подробное объяснение, почему это происходит / что здесь происходит? (подсказка: data.frame пытается использовать имя предоставленной переменной в качестве имени элемента во фрейме данных)
  • 0
    Просто подумал, может ли это быть ошибкой? И если да, то как и где мне сообщить об этом?
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню