Чем отличаются операторы присваивания =
и <-
в R?
Как показывает ваш пример, =
и <-
имеют слегка различающийся приоритет операторов (который определяет порядок вычисления, когда они смешиваются в одном выражении). Фактически, ?Syntax
в R дает следующую таблицу приоритетов операторов, от самого высокого до самого низкого:
…
‘-> ->>’ rightwards assignment
‘<- <<-’ assignment (right to left)
‘=’ assignment (right to left)
…
Но это единственная разница?
Поскольку вы спрашивали об операторах присваивания : да, это единственное отличие. Тем не менее, вы были бы прощены за то, что поверили в обратное. Даже документация R ?assignOps
утверждает, что есть больше различий:
Оператор <-
может использоваться где угодно,
тогда как оператор =
разрешен только на верхнем уровне (например,
в полном выражении, набранном из командной строки) или как единое целое
подвыражений в ограниченном списке выражений.
Давайте не будем особо подчеркивать: Документация R (тонко) неверна [ 1 ] . Это легко показать: нам просто нужно найти контрпример примера оператора =
, который не является (a) на верхнем уровне и (b) подвыражением в ограниченном списке выражений (т.е. {…; …}
). - Без лишних слов:
x
# Error: object 'x' not found
sum((x = 1), 2)
# [1] 3
x
# [1] 1
Очевидно, что мы выполнили задание, используя =
, вне контекста (a) и (b). Итак, почему документация о ядре языка R была неправильной в течение десятилетий?
Это потому, что в синтаксисе R символ =
имеет два разных значения, которые обычно смешиваются:
- Первое значение как оператор присваивания . Это все, о чем мы говорили до сих пор.
- Второе значение - это не оператор, а синтаксический маркер , который сигнализирует именованный аргумент, передавая в вызове функции. В отличие от оператора
=
, он не выполняет никаких действий во время выполнения, он просто изменяет способ анализа выражения.
Посмотрим.
В любом куске кода общего вида…
<i>‹function_name›</i>(<i>‹argname›</i> <b>=</b> <i>‹value›</i>, …)
<i>‹function_name›</i>(<i>‹args›</i>, <i>‹argname›</i> <b>=</b> <i>‹value›</i>, …)
… =
- это токен, определяющий передачу именованного аргумента: это , а не оператор присваивания. Кроме того, =
полностью запрещено в некоторых синтаксических контекстах:
if (<i>‹var›</i> <b>=</b> <i>‹value›</i>) …
while (<i>‹var›</i> <b>=</b> <i>‹value›</i>) …
for (<i>‹var›</i> <b>=</b> <i>‹value›</i> in <i>‹value2›</i>) …
for (<i>‹var1›</i> in <i>‹var2›</i> <b>=</b> <i>‹value›</i>) …
Любое из них вызовет ошибку «неожиданно» = «in‹ bla ›».
В любом другом контексте =
относится к вызову оператора присваивания. В частности, простое размещение скобок вокруг подвыражения делает любое из вышеперечисленного (а) действительным и (б) присваиванием . Например, следующее выполняет присваивание:
median((x = 1 : 10))
Но также:
if (! (nf = length(from))) return()
Теперь вы можете возразить, что такой код ужасен (и вы можете быть правы). Но я взял этот код из функции base::file.copy
(заменив <-
на =
) - это распространенный шаблон в большей части базовой кодовой базы R.
Оригинальное объяснение Джона Чемберса , на котором, вероятно, основана документация R, действительно объясняет это правильно:
[=
присваивание разрешено] разрешено только в двух местах грамматики: на верхнем уровне (в виде законченной программы или пользовательского выражения); и когда он изолирован от окружающей логической структуры, скобками или дополнительной парой скобок.
Признание: я солгал раньше. - это еще одно отличие между операторами =
и <-
: они вызывают разные функции. По умолчанию эти функции делают то же самое, но вы можете переопределить любую из них отдельно, чтобы изменить поведение. Напротив, <-
и ->
(присвоение слева направо), хотя и синтаксически различны, всегда вызывают одинаковую функцию. Переопределение одного переопределяет другое. Знание этого редко бывает практичным , но можно использовать для некоторых забавных махинаций .