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
帮助页面上所示。
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 如何确定给定用法=
是引用运算符还是传递命名参数?让我们来看看。
在一般形式的任何代码中……
<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 核心代码库中,这是一种普遍的模式。
R 文档可能基于 John Chambers 所作的原始解释,它实际上正确地解释了这一点:
[
=
赋值] 仅在语法的两个位置允许:顶级(作为完整程序或用户键入的表达式);当与周围的逻辑结构隔离时,可以使用大括号或额外的一对括号。
总之,默认情况下,运算符<-
和=
执行相同的操作。但是它们中的任何一个都可以单独覆盖以更改其行为。相比之下, <-
和->
(从左到右的赋值)尽管在语法上是不同的,但始终调用相同的函数。覆盖一个也覆盖另一个。知道这一点很少是实用的,但可以用于一些有趣的恶作剧。
Google 的 R 风格指南通过禁止分配 “=” 来简化此问题。不错的选择。
https://google.github.io/styleguide/Rguide.xml
R 手册详细介绍了所有 5 个赋值运算符。
http://stat.ethz.ch/R-manual/R-patched/library/base/html/assignOps.html