Чем отличаются операторы присваивания "=" и "<-" в 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 символ =
имеет два разных значения, которые обычно объединяются:
- Первое значение - это оператор присваивания. Об этом до сих пор говорили все.
- Второе значение не является оператором, а скорее синтаксическим токеном, который передает именованный аргумент, передаваемый в вызове функции. В отличие от оператора
=
он не выполняет никаких действий во время выполнения, он просто изменяет способ анализа выражения.
Посмотрим.
В любом фрагменте кода общей формы...
‹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
.
Ответ 3
Руководство по стилю Google R упрощает эту проблему, запрещая присвоение "=". Неплохой выбор.
https://google.github.io/styleguide/Rguide.xml
В руководстве R содержатся подробные сведения обо всех 5 операторах присваивания.
http://stat.ethz.ch/R-manual/R-patched/library/base/html/assignOps.html
Ответ 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