在R中,赋值运算符=
和<-
有何不同?
我知道这两个运算符略有不同,就像下面的例子所示:
x <- y <- 5
x = y = 5
x = y <- 5
x <- y = 5
# Error in (x <- y) = 5 : could not find function "<-<-"
但这是唯一的区别吗?
在R中,赋值运算符=
和<-
有何不同?
我知道这两个运算符略有不同,就像下面的例子所示:
x <- y <- 5
x = y = 5
x = y <- 5
x <- y = 5
# Error in (x <- y) = 5 : could not find function "<-<-"
但这是唯一的区别吗?
当您在函数调用中使用赋值运算符来设置参数值时,赋值运算符的差异更加明显。例如:
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 都有键盘快捷键来方便地输入 <-
。在 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)
这是因为,如?Syntax
页面所示,=
的优先级低于<-
。
x <- x = 5
如何被解释的说明稍有错误:实际上,R 将其解释为 `<-<-`(x, y = 5, value = 5)
(这本身多少等价于 tmp <- x; x <- `<-<-`(tmp, y = 5, value = 5)
)。糟糕! - Konrad Rudolph=
在函数调用中的使用 并不执行赋值,也不是一个赋值运算符。它是一个完全独立的 R 表达式,在这个表达式中仅仅是使用了相同的字符。此外,您展示的代码并没有在函数作用域中“声明” x
。函数声明 才能进行该声明。函数调用不会(使用命名的 ...
参数将会变得更加复杂)。 - Konrad Rudolph=
是一个任意的分隔符标记,在解析后不再存在。在解析 f(x = 1)
后,R 看到(基本上)call("f", 1)
。而对于 x = 1
,R 看到的是 call("=", "x", 1)
。确实,在两种情况下名称绑定也会发生,但是对于赋值运算符,它会在调用赋值运算符函数后发生。 - Konrad Rudolph=
和 <-
的区别在哪里?如您的示例所示,它们具有略微不同的操作符优先级(确定它们在同一表达式中混合时的求值顺序)。实际上,R中的?Syntax
给出以下操作符优先级表,从高到低:…
‘-> ->>’ rightwards assignment
‘<- <<-’ assignment (right to left)
‘=’ assignment (right to left)
…
但这是唯一的区别吗??assignOps
的说明声称存在更多的不同之处:
让我们不要太苛求:R文档是错误的。这很容易证明:我们只需要找到一个反例来说明这个运算符
<-
可以用在任何地方, 而运算符=
只允许在顶层(例如,在命令提示符下键入的完整表达式)或作为一个表达式列表中的子表达式之一。
=
运算符既不在顶层,也不是作为表达式列表中的子表达式之一(即{…; …}
)。 —无需多言:x
# Error: object 'x' not found
sum((x = 1), 2)
# [1] 3
x
# [1] 1
显然我们在上下文(a)和(b)之外使用了=
进行赋值。那么,为什么核心R语言特性的文档几十年来一直是错误的呢?
这是因为在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>) …
任何一个都将引发错误“在‹bla›中出现意外的'='”。
在任何其他上下文中,=
指的是赋值运算符调用。特别地,仅仅在子表达式周围放置括号就可以使上述任何一个(a)有效,并且(b)是一个赋值。例如,以下代码执行了赋值:
median((x = 1 : 10))
但是也:
if (! (nf = length(from))) return()
现在你可能会反对这样的代码是极其糟糕的(你可能是对的)。但我从base::file.copy
函数中取出了这段代码(将<-
替换为=
)—— 这是许多核心R代码库中普遍存在的模式。
John Chambers的原始解释,可能是R文档基于此的,实际上正确地解释了这个问题:
[
=
赋值语句] 只允许在两个语法结构中使用:在顶层(作为完整程序或用户键入的表达式)和当它与周围逻辑结构隔离时,通过大括号或额外的一对圆括号。
总之,默认情况下,运算符<-
和=
执行相同的操作。但是,它们中的任何一个都可以单独被覆盖以更改它的行为。相比之下,<-
和 ->
(从左到右赋值)虽然在语法上不同,但始终调用相同的函数。覆盖一个也会覆盖另一个。了解这一点很少实用但它可以用于一些有趣的小把戏。
?
的优先级实际上位于=
和<-
之间,在覆盖?
时具有重要影响,但在其他情况下几乎没有影响。 - moodymudskipper=
称为重载运算符也很令人困惑:在R中,“运算符”一词(至少在R 4.0之前)具有特定的含义,指的是具有特殊语法规则的函数调用。当用于将名称绑定到函数调用参数列表中的参数名称时,=
并不是在执行调用操作。在这种情况下,=
只是一个语法标记(如;
),而不是运算符。 - Konrad Rudolph=
标记在解析阶段后完全消失,它在解析树中根本没有表示,也不会被评估。在R(以及其他语言中)中,赋值和函数调用中的名称绑定基本上是以不同的方式发生的,它不仅仅是“时间上的区别”,也不是由于运算符优先级引起的。 - Konrad Rudolph谷歌的 R 代码规范通过禁止使用“=”符号进行赋值来简化问题,这是一个不错的选择。
https://google.github.io/styleguide/Rguide.xml
R 手册详细介绍了所有 5 种赋值运算符。
http://stat.ethz.ch/R-manual/R-patched/library/base/html/assignOps.html
x
是否小于-y
,则可以编写if (x<-y)
,它不会发出警告或错误,并且看起来运行良好。但是仅当y=0
时,它才是“FALSE”。 - Matt Dowle=
,为什么还要用<-
来伤害你的眼睛和手指呢?99.99%的情况下,使用=
就足够了。不过有时你需要使用<<-
,那就是另一回事了。 - Fernando<-
操作符。 - undefinedx = 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
获取优先级(绑定)和分组规则。根据John Chambers的说法,运算符=
只允许在"顶层"使用,这意味着它在控制结构(如if
)中是不允许使用的,因此以下编程错误是非法的。
> if(x = 0) 1 else x
Error: syntax error
他写道,“在控制表达式中禁止使用新的赋值形式 [=] 可以避免编程错误(如上面的例子),这些错误比其他S赋值操作符更容易发生。”if ((x = 0)) 1 else x
将起作用。运算符
<-
和=
将赋值给它们所在的环境。 运算符<-
可以在任何地方使用,而运算符=
仅允许在顶层使用(例如,在命令提示符处键入的完整表达式)或作为表达式列表中的子表达式之一。
x <- 42
是一条语句;在 if (x <- 42) {}
中它将成为一个表达式,并且是无效的。需要明确的是,这与您是否在全局环境中无关。 - Steve Pitchers1 + (x = 2)
。 - Pavel Minaevfunction() x = 1
,repeat x = 1
,if (TRUE) x = 1
中... - moodymudskipperdf <- 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
我不确定是否在此处引用了Patrick Burns的书《R地狱》中的内容,其中在8.2.26 =不是<-的同义词中,Patrick指出:“当你想设置函数参数时,显然不想使用'<-'。”该书可在https://www.burns-stat.com/documents/books/the-r-inferno/获取。
在过去的R版本或者R的前身语言S语言中,<-
和=
之间存在一些差异。但是目前看来,仅使用=
就像其他现代语言(如Python、Java)一样不会引起任何问题。当您同时传递一个值到某些参数并创建全局变量时,使用<-
可以实现一些更多的功能,但它可能会产生奇怪/不想要的行为,例如:
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
=
和<-
之间所谓差异的常见误解。因此,这个解释是错误的。请参见我的回答,对这个有害的谬论进行详尽的纠正。明确地说:您可以将第一段代码重写为使用=
而不是<-
,而不改变其含义:df <- data.frame(a = rnorm(10), (b = rnorm(10)))
。就像<-
一样,=
也是一个返回值的函数。 - Konrad Rudolph=
和<-
之间的区别是因为他们好奇为什么R
相比于其他流行的科学/数学语言(如Python)有多个赋值运算符。此外,(
在R中也是一个函数,因此从技术上讲,(b = rnorm(10))
与b <- rnorm(10)
不同,因为您可以在代码中覆盖(
函数的含义。 - Chunhui Gu=
”的答案是“是”。其他所有内容,特别是您声称在某些情况下无法使用=
的说法,都是不正确的。是的,当然可以重写(
,就像可以重写=
和<-
一样,因此,从技术上讲,您可以重新定义它们,使它们不再相同。但是您肯定会同意,这是一个纯粹的干扰。 - Konrad Rudolph