R中的赋值运算符=和<-有什么区别?
如您的示例所示,= 和 <- 的运算符优先级略有不同(这决定了它们在同一表达式中混合时的计算顺序)。事实上,R 中的?Syntax 给出了以下运算符优先级表,从高到低:
…
‘-> ->>’ rightwards assignment
‘<- <<-’ assignment (right to left)
‘=’ assignment (right to left)
…
但这是唯一的区别吗?
既然您问的是 赋值运算符:是的,这是唯一的区别。但是,如果您不相信,您会被原谅。甚至?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›, …)
…= 是定义命名参数传递的标记:它 不是 赋值运算符。此外,= 在某些句法上下文中完全禁止:
if (‹var› = ‹value›) …
while (‹var› = ‹value›) …
for (‹var› = ‹value› in ‹value2›) …
for (‹var1› in ‹var2› = ‹value›) …
其中任何一个都会引发错误“在‹bla›中出现意外的'='”。
在任何其他上下文中,= 指的是赋值运算符调用。特别是,仅在子表达式周围加上括号就可以使上述 (a) 中的任何一个都有效,并且 (b) 成为 assignment。例如,以下执行赋值:
median((x = 1 : 10))
还有:
if (! (nf = length(from))) return()
现在你可能会反对这样的代码很糟糕(你可能是对的)。但是我从 base::file.copy 函数中获取了这段代码(将 <- 替换为 =)——这是大多数核心 R 代码库中普遍存在的模式。
R 文档可能基于的original explanation by John Chambers 实际上正确地解释了这一点:
[= 赋值] 只允许在语法中的两个地方:在顶层(作为完整的程序或用户键入的表达式);并且当与周围的逻辑结构隔离时,使用大括号或一对额外的括号。
总而言之,默认情况下,<- 和 = 运算符执行相同的操作。但是它们中的任何一个都可以单独覆盖以改变其行为。相比之下,<- 和 ->(从左到右赋值)虽然在语法上不同,但总是调用 same 函数。覆盖一个也覆盖另一个。知道这一点很少实用but it can be used for some fun shenanigans。