协慌网

登录 贡献 社区

R 中的 “=” 和“<-”赋值运算符有什么区别?

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 的语法中,符号=具有两个通常会被混淆的含义(即使是专家,包括上面引用的文档中的含义):

  1. 第一个含义是作为赋值运算符。到目前为止,我们已经讨论过这一切。
  2. 第二个含义不是运算符,而是语法令牌,该令牌表示已通过函数调用传递的命名实参。=运算符不同,它在运行时不执行任何操作,它仅更改表达式的解析方式。

那么,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