在R中赋值运算符=和<-之间有什么区别?

我知道操作符略有不同,如本例所示

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

但这是唯一的不同吗?


来自官方R文档:

操作符<-和=赋值到它们所处的环境 进行了评估。操作符<-可以在任何地方使用,而 操作符=只允许在顶层(例如,在 在命令提示符处键入的完整表达式)或作为 带括号的表达式列表中的子表达式。


在函数调用中使用赋值操作符来设置实参值时,赋值操作符之间的区别更加明显。例如:

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是在用户工作区中声明的,因此可以在函数调用完成后使用它。


为了兼容(非常)老版本的S-Plus, R社区普遍倾向于使用<-进行赋值(函数签名除外)。注意,空格有助于阐明情况,如

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

大多数R ide都有快捷键,使<-更容易输入。Architect中的Ctrl + =, RStudio中的Alt + - (macOS下的Option + -), emacs+ESS中的Shift + -(下划线)。


如果你更喜欢写=而不是<-,但想要为公开发布的代码(例如在CRAN上)使用更常见的赋值符号,那么你可以使用formatR包中的tidy_*函数之一自动将=替换为<-。

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)

这是因为=的优先级低于<-,请参见?语法帮助页面。


谷歌的R样式指南通过禁止赋值使用“=”简化了这个问题。不错的选择。

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

R手册详细介绍了所有5个赋值操作符。

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


根据John Chambers的说法,操作符=只允许在“顶层”,这意味着它不允许在像if这样的控制结构中,这使得下面的编程错误是非法的。

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

正如他所写的,“在控制表达式中禁止新的赋值形式[=]可以避免编程错误(如上面的例子),与其他S赋值相比,相等操作符更有可能出现这种错误。”

如果它“通过大括号或一对额外的圆括号与周围的逻辑结构隔离”,则可以做到这一点,因此if ((x = 0)) 1否则x就可以工作。

参见http://developer.r-project.org/equalAssign.html


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。


这也有助于理解这两个操作符之间的区别:

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


在R中赋值运算符=和<-之间有什么区别?

如示例所示,=和<-的运算符优先级略有不同(当它们混合在同一个表达式中时,运算符优先级决定了求值的顺序)。事实上,R中的?语法给出了以下操作符优先级表,从高到低:

... ' -> ->> '向右分配 ' <- <<- '赋值(从右向左) ' = '赋值(从右到左) ...

但这是唯一的不同吗?

既然你问的是赋值操作符:是的,这是唯一的区别。然而,如果你不这么认为,也情有可原。甚至?assignOps的R文档也声称有更多的区别:

操作符<-可以在任何地方使用, 而运算符=只允许在顶层(例如, 在命令提示符处输入的完整表达式中)或作为一个 一个带括号的表达式列表中的子表达式。

让我们不要过分强调它:R文档是错误的。这很容易显示:我们只需要找到一个=运算符的反例,它既不是(a)在顶层,也不是(b)在一个带括号的表达式列表中的子表达式(例如{…;})。-闲话不多说:

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

显然,我们已经在上下文(a)和(b)之外执行了赋值,使用=。那么,为什么R语言核心特性的文档几十年来一直是错误的呢?

这是因为在R的语法中,符号=有两个不同的含义,经常被混为一谈(甚至是专家,包括在上面引用的文档中):

第一个含义是赋值操作符。这是我们目前为止讨论的全部内容。 第二个含义不是一个操作符,而是一个语法标记,表示在函数调用中传递的命名参数。与=运算符不同,它在运行时不执行任何操作,它只是改变表达式的解析方式。

那么R如何决定=的给定用法是指操作符还是指定参数传递?让我们来看看。

在任何一段一般形式的代码中…

‹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›) …

任何这些都会引发错误“unexpected”=“in”。

在任何其他上下文中,=指赋值操作符调用。特别是,只要在子表达式周围加上括号,就可以使上面的(a)有效,(b)为赋值。例如,下面执行赋值:

median((x = 1 : 10))

但也:

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

现在您可能会反对说这样的代码很糟糕(您可能是对的)。但是我从base::文件中获取了这段代码。复制函数(用=替换<-)——这是R核心代码库中普遍存在的模式。

约翰·钱伯斯(John Chambers)最初的解释,也就是R文档可能基于的解释,实际上正确地解释了这一点:

[=赋值]只允许出现在语法中的两个地方:在顶层(作为一个完整的程序或用户类型的表达式);当与周围的逻辑结构隔离时,通过大括号或额外的一对括号。


总的来说,默认情况下,操作符<-和=做同样的事情。但它们中的任何一个都可以被单独覆盖以改变其行为。相比之下,<-和->(从左到右赋值)虽然语法不同,但总是调用相同的函数。覆盖一个也会覆盖另一个。知道这一点很少是实际的,但它可以用于一些有趣的恶作剧。


我不确定Patrick Burns的书R inferno是否在8.2.26中被引用,其中=不是<的同义词- Patrick说“当你想设置一个函数的参数时,你显然不想使用'<-'。”这本书可以在https://www.burns-stat.com/documents/books/the-r-inferno/上买到


在R的过去版本甚至R的前身语言(S语言)中,<-和=之间存在一些差异。但目前,像使用其他现代语言(python, java)一样使用=似乎不会引起任何问题。你可以通过使用<-来实现更多的功能,当传递一个值给一些增强,同时也创建一个全局变量,但它可能会有奇怪的/不想要的行为,如in

df <- data.frame(
      a = rnorm(10),
      b <- rnorm(10)
)
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 ...

强烈推荐!试着阅读这篇文章,这是试图解释这两者区别的最好的文章: 检查https://colinfay.me/r-assignment/

此外,可以将<-看作一个无形地返回一个值的函数。

a <- 2
(a <- 2)
#> [1] 2

参见:https://adv-r.hadley.nz/functions.html