"="和"<-"赋值操作符有什么区别?

R中赋值操作符=<-有什么区别?

我知道运算符略有不同,如这个例子所示

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

但这是唯一的区别吗?

333954 次浏览

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


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_*函数之一自动将=替换为<-

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帮助页面所示。

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(x = 0) 1 else xError: syntax error

正如他所写的那样,“在控制表达式中禁止新的赋值形式[=]可以避免编程错误(例如上面的示例),这些错误在使用相等运算符时比使用其他S赋值时更容易发生。”

如果它“通过大括号或额外的一对括号与周围的逻辑结构隔离”,您可以设法做到这一点,因此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不一样,它不起作用!含义:将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中的?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的语法中,符号=有两个不同的含义,通常会被合并(即使是专家,包括上面引用的留档):

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

那么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代码库中普遍存在的模式。

R留档可能基于的约翰·钱伯斯的原始解释实际上正确地解释了这一点:

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


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

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