R中赋值操作符=和<-有什么区别?
=
<-
我知道运算符略有不同,如这个例子所示
x <- y <- 5x = y = 5x = y <- 5x <- y = 5# Error in (x <- y) = 5 : could not find function "<-<-"
但这是唯一的区别吗?
官方R留档:
运算符<-和=分配到它们所在的环境中运算符<-可以在任何地方使用,而运算符=只允许在顶层(例如,在在命令提示符下键入的完整表达式)或作为在带括号的表达式列表中的子表达式。
当您在函数调用中使用它们设置参数值时,赋值操作符中的差异更明显。例如:
median(x = 1:10)x## Error: object 'x' not found
在这种情况下,x在函数的范围内声明,因此它不存在于用户工作区中。
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+=在Architect中,Alt+-在RStudio中(选项+-在macOS中),Shift+-(下划线)在emacs+ESS中。
如果您更喜欢编写=到<-,但希望对公开发布的代码(例如在CRAN上)使用更常见的赋值符号,那么您可以使用formatR包中的tidy_*函数之一自动将=替换为<-。
formatR
tidy_*
library(formatR)tidy_source(text = "x=1:5", arrow = TRUE)## x <- 1:5
“为什么x <- y = 5抛出错误而不是x <- y <- 5?”这个问题的答案是“这取决于解析器中包含的魔力”。R的语法包含许多模棱两可的案例,必须以这种或那种方式解析。解析器选择根据使用的是=还是<-以不同的顺序解析表达式的位。
x <- y = 5
x <- y <- 5
要了解发生了什么,您需要知道赋值会以静默方式返回被赋值的值。您可以通过显式打印(例如print(x <- 2 + 3))更清楚地看到这一点。
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帮助页面所示。
?Syntax
Google的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
> if(x = 0) 1 else xError: syntax error
正如他所写的那样,“在控制表达式中禁止新的赋值形式[=]可以避免编程错误(例如上面的示例),这些错误在使用相等运算符时比使用其他S赋值时更容易发生。”
如果它“通过大括号或额外的一对括号与周围的逻辑结构隔离”,您可以设法做到这一点,因此if ((x = 0)) 1 else x可以工作。
if ((x = 0)) 1 else x
见http://developer.r-project.org/equalAssign.html
x = y = 5等效于x = (y = 5),因为赋值操作符“group”从右向左,这有效。含义:将5分配给y,留下数字5;然后将5分配给x。
x = y = 5
x = (y = 5)
y
这与(x = y) = 5不一样,它不起作用!含义:将y的值分配给x,留下y的值;然后将5分配给,嗯…,到底是什么?
(x = y) = 5
当您混合不同类型的赋值操作符时,<-比=绑定得更紧。所以x = y <- 5被解释为x = (y <- 5),这是有意义的情况。
x = y <- 5
x = (y <- 5)
不幸的是,x <- y = 5被解释为(x <- y) = 5,这是不起作用的情况!
(x <- y) = 5
有关优先级(绑定)和分组规则,请参阅?Syntax和?assignOps。
?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中的?Syntax给出了以下运算符优先级表,从最高到最低:
…‘-> ->>’ rightwards assignment‘<- <<-’ assignment (right to left)‘=’ assignment (right to left)…
既然你问的是赋值操作符:是的,这是唯一的区别。然而,如果你不这么认为,那是情有可原的。甚至?assignOps的R留档也声称有更多的差异:
运算符<-可以在任何地方使用,而运算符=仅允许在顶层(例如,在命令提示符下键入的完整表达式中)或作为一个表达式列表中的子表达式。
让我们不要把它说得太好:R留档错了。这很容易显示:我们只需要找到=运算符的反例,它不是(a)在顶层,也不是(b)在带括号的表达式列表中的子表达式(即{…; …})。-无需进一步说明:
{…; …}
x# Error: object 'x' not foundsum((x = 1), 2)# [1] 3x# [1] 1
显然,我们已经在上下文(a)和(b)之外使用=执行了赋值。那么,为什么核心R语言功能的留档几十年来一直是错误的呢?
这是因为在R的语法中,符号=有两个不同的含义,通常会被合并(即使是专家,包括上面引用的留档):
那么R如何决定=的给定用法是指运算符还是指传递的命名参数呢?让我们看看。
在一般形式的任何一段代码中…
‹function_name›(‹argname› = ‹value›, …)‹function_name›(‹args›, ‹argname› = ‹value›, …)
…=是定义命名参数传递的标记:它是没有赋值操作符。此外,=在某些语法上下文中完全是禁止:
if (‹var› = ‹value›) …while (‹var› = ‹value›) …for (‹var› = ‹value› in ‹value2›) …for (‹var1› in ‹var2› = ‹value›) …
其中任何一个都会在bla中引发错误“意外'='”。
在任何其他上下文中,=指的是赋值操作符调用。特别是,仅在子表达式周围加上括号即可使上述(a)和(b)中的任何一个有效。例如,以下执行赋值:
median((x = 1 : 10))
但也:
if (! (nf = length(from))) return()
现在你可能会反对这样的代码是残暴的(你可能是对的)。但我从base::file.copy函数中获取了这段代码(用=替换<-)-这是大多数核心R代码库中普遍存在的模式。
base::file.copy
R留档可能基于的约翰·钱伯斯的原始解释实际上正确地解释了这一点:
[=赋值]只允许在语法中的两个地方:顶层(作为完整的程序或用户键入的表达式);当与周围的逻辑结构隔离时,通过大括号或额外的一对括号。
总之,默认情况下,运算符<-和=做同样的事情。但它们都可以单独为覆盖来改变其行为。相比之下,<-和->(从左到右赋值)尽管语法上不同,但总是调用相同函数。覆盖一个也覆盖另一个。知道这很少实用但它可以用于一些有趣的恶作剧。
->
我不确定Patrick Burns的书《R地狱》是否在这里被引用过,在8.2.26=不是<-的同义词中Patrick说“当你想设置函数的参数时,你显然不想使用'<-'。”这本书在https://www.burns-stat.com/documents/books/the-r-inferno/处可用