Чем отличаются операторы присваивания "=" и "<-" в R?

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

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

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

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

Ответы

Ответ 1

Каковы различия между операторами присваивания = и <- в 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-документации, на самом деле объясняет это правильно:

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


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

Ответ 2

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

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

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

Ответ 5

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

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

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

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

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

Ответ 6

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

Ответ 7

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

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