【问题标题】:What are the differences between "=" and "<-" assignment operators in R?R中的“=”和“<-”赋值运算符有什么区别?
【发布时间】:2010-12-17 01:01:30
【问题描述】:

R中的赋值运算符=&lt;-有什么区别?

我知道运算符略有不同,如本例所示

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

但这是唯一的区别吗?

【问题讨论】:

  • here 所述,&lt;- 符号的起源来自实际上只有一个 &lt;- 键的旧 APL 键盘。

标签: r assignment-operator r-faq


【解决方案1】:

当您使用assignment operators 在函数调用中设置参数值时,它们的区别会更加明显。例如:

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


为了与(非常)旧版本的 S-Plus 兼容,R 社区普遍倾向于使用 &lt;- 进行赋值(函数签名除外)。请注意,空格有助于阐明诸如

之类的情况
x<-3
# Does this mean assignment?
x <- 3
# Or less than?
x < -3

大多数 R IDE 都有键盘快捷键,使&lt;- 更易于键入。 Ctrl + = 在 Architect 中,Alt + - 在 RStudio 中(Option + - 在 macOS 下),Shift + -(下划线)在 emacs+ESS 中。


如果您更喜欢将= 写成&lt;-,但希望对公开发布的代码(例如在 CRAN 上)使用更常见的赋值符号,那么您可以使用 @987654336 中的 tidy_* 函数之一@包自动将=替换为&lt;-

library(formatR)
tidy_source(text = "x=1:5", arrow = TRUE)
## x <- 1:5

“为什么x &lt;- y = 5 抛出错误而不是x &lt;- y &lt;- 5?”问题的答案是“这取决于解析器中包含的魔法”。 R 的语法包含 many ambiguous cases 必须以一种或另一种方式解决。解析器根据使用的是= 还是&lt;-,选择以不同的顺序解析表达式的位。

要了解发生了什么,您需要知道赋值会默默地返回所分配的值。您可以通过显式打印更清楚地看到这一点,例如print(x &lt;- 2 + 3)

其次,如果我们使用前缀表示法进行赋值会更清楚。所以

x <- 5
`<-`(x, 5)  #same thing

y = 5
`=`(y, 5)   #also the same thing

解析器将x &lt;- y &lt;- 5解释为

`<-`(x, `<-`(y, 5))

我们可能预计x &lt;- y = 5 会是

`<-`(x, `=`(y, 5))

但实际上它被解释为

`=`(`<-`(x, y), 5)

这是因为= 的优先级低于&lt;-,如?Syntax 帮助页面所示。​​

【讨论】:

  • Patrick Burns 在The R Inferno 的第 8.2.26 章中也提到了这一点(不是我,而是建议)
  • 但是median((x = 1:10))median(x &lt;- 1:10)的效果是一样的。
  • 我真的不认为它们是快捷方式,无论如何你按相同数量的键
  • 我刚刚意识到您对 x &lt;- x = 5 如何被解释的解释有点错误:实际上,R 将其解释为 ​`&lt;-&lt;-`(x, y = 5, value = 5)(它本身或多或少等同于 tmp &lt;- x; x &lt;- `&lt;-&lt;-`(tmp, y = 5, value = 5))。哎呀!
  • ... 我刚刚意识到这个答案的第一部分是不正确的,不幸的是,它非常具有误导性,因为它延续了一个常见的误解:在函数调用中使用 = 的方式 不执行赋值,也不是赋值运算符。这是一个完全不同的解析 R 表达式,只是碰巧使用了相同的字符。此外,您显示的代码并未在函数范围内“声明”x函数声明执行所述声明。函数调用没有(命名为 ... 参数会变得有点复杂)。
【解决方案2】:

R中的赋值运算符=&lt;-有什么区别?

如您的示例所示,=&lt;- 的运算符优先级略有不同(这决定了它们在同一表达式中混合时的计算顺序)。事实上,R 中的?Syntax 给出了以下运算符优先级表,从高到低:

…
‘-> ->>’           rightwards assignment
‘<- <<-’           assignment (right to left)
‘=’                assignment (right to left)
…

但这是唯一的区别吗?

既然您问的是 赋值运算符:是的,这是唯一的区别。但是,如果您不相信,您会被原谅。甚至?assignOps 的 R 文档也声称存在更多差异:

运算符&lt;-可以在任何地方使用, 而运算符= 只允许在顶层使用(例如, 在命令提示符下键入的完整表达式中)或作为一个 表达式的花括号列表中的子表达式。

我们不要过分强调: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如何决定=的给定用法是指操作符还是命名参数传递?让我们看看。

在任何一般形式的代码中......

‹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 函数中获取了这段代码(将 &lt;- 替换为 =)——这是大多数核心 R 代码库中普遍存在的模式。

R 文档可能基于的original explanation by John Chambers 实际上正确地解释了这一点:

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


总而言之,默认情况下,&lt;-= 运算符执行相同的操作。但是它们中的任何一个都可以单独覆盖以改变其行为。相比之下,&lt;--&gt;(从左到右赋值)虽然在语法上不同,但总是调用 same 函数。覆盖一个也覆盖另一个。知道这一点很少实用but it can be used for some fun shenanigans

【讨论】:

  • 关于 R 文档中的优先级和错误,? 的优先级实际上正好在=&lt;- 之间,这在覆盖? 时会产生重要影响,而且几乎没有否则。
  • @Moody_Mudskipper 这太奇怪了!您似乎是对的,但根据源代码 (main/gram.y),? 的优先级已正确记录,并且低于=&lt;-。跨度>
  • 我不会说 C 但我想= 在构建解析树之前会得到特殊处理。可能与函数参数有关,在 foo(x = a ? b) 中我们会在解析表达式的其余部分之前查找 = 是有道理的。
  • @Moody_Mudskipper I’ve asked r-devel
  • @Moody_Mudskipper FWIW 这最终在 4.0.0 中得到修复。
【解决方案3】:

Google 的 R 风格指南通过禁止“=”进行赋值来简化问题。不错的选择。

https://google.github.io/styleguide/Rguide.xml

R 手册详细介绍了所有 5 个赋值运算符。

http://stat.ethz.ch/R-manual/R-patched/library/base/html/assignOps.html

【讨论】:

  • x&lt;-y 意外分配的缺点是 x &lt; -y 的意思,这让我非常烦恼,我个人更喜欢 =。让你的代码依赖于存在的空格对我来说似乎并不好。建议将间距作为样式建议是可以的,但是无论是否存在空格,您的代码都会以不同的方式运行?如果你重新格式化你的代码,或者使用搜索和替换,空白有时会消失,代码会出错。这不是= 的问题。 IIUC,禁止=等于要求“&lt;- ”;即 3 个字符,包括一个空格,而不仅仅是“&lt;-”。
  • 请注意,R 将任何非 0 视为TRUE。因此,如果您打算测试x 是否小于-y,您可以编写if (x&lt;-y),它不会发出警告或错误,并且似乎工作正常。不过,当y=0 时,它只会是FALSE
  • 如果您确实禁止= 并使用&lt;- ,那么很难说不需要grep "[^&lt;]&lt;-[^ ]" *.R 的额外步骤。 = 不需要这样的grep
  • 如果可以使用=,为什么要用&lt;- 伤害你的眼睛和手指?在 99.99% 的情况下,= 没问题。但有时你需要&lt;&lt;-,这是不同的历史。
【解决方案4】:

x = y = 5 等价于x = (y = 5),因为赋值运算符从右到左“分组”,这是有效的。含义:将5分配给y,留下数字5;然后将该 5 分配给x

这和(x = y) = 5不一样,这不行!含义:将y的值赋值给x,留下y的值;然后将 5 分配给,嗯...,到底是什么?

当您混合使用不同类型的赋值运算符时,&lt;- 的绑定比 = 更紧密。所以x = y &lt;- 5 被解释为x = (y &lt;- 5),这是有道理的。

很遗憾,x &lt;- y = 5 被解释为(x &lt;- y) = 5,这样就不行了!

有关优先级(绑定)和分组规则,请参阅 ?Syntax?assignOps

【讨论】:

  • 是的,正如Konrad Rudolph 的回答所说,&lt;- &lt;&lt;- 在优先表中高于=,这意味着&lt;- 将首先被执行。所以,x &lt;- y = 5 应该被执行为(x &lt;- y) = 5
  • @Nick Dong 确实如此。有帮助的是,?Syntax {base} 中明确记录了运算符优先级表。
【解决方案5】:

根据 John Chambers 的说法,运算符 = 只允许在“顶层”使用,这意味着它不允许在像 if 这样的控制结构中使用,从而导致以下编程错误非法。

> if(x = 0) 1 else x
Error: syntax error

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

如果它“通过大括号或一对额外的括号与周围的逻辑结构隔离开来”,您可以设法做到这一点,因此if ((x = 0)) 1 else x 可以工作。

http://developer.r-project.org/equalAssign.html

【讨论】:

  • 这是一个常见的错误,x==0 几乎总是意味着。
  • 啊,是的,我忽略了你说的“编程错误”。这实际上是一个好消息,这会导致错误。并且有充分的理由更喜欢 x=0 作为分配而不是 x&lt;-0
  • 是的,很高兴这会导致错误,尽管我吸取了不同的教训来选择更喜欢什么;我选择尽可能少地使用=,因为=== 看起来非常相似。
  • 这个例子的呈现方式对我来说太奇怪了。 if(x = 0) 1 else x 抛出错误,帮助我找到并纠正错误。 if(x &lt;- 1) 1 else x 不会抛出错误,而且非常混乱。
  • 我的意思是,一个真正有用的错误检查器会在那里抛出一个错误并说“你有无用的代码,总是会返回 else 值,你的意思是写那样吗?”,但是,这可能是一个白日梦......
【解决方案6】:

运算符&lt;-= 分配到评估它们的环境中。运算符&lt;- 可以在任何地方使用,而运算符= 只允许在顶层(例如,在命令提示符下键入的完整表达式中)或作为表达式括号列表中的子表达式之一。

【讨论】:

  • 我认为“顶级”是指语句级别,而不是表达式级别。所以x &lt;- 42 本身就是一个声明;在if (x &lt;- 42) {} 中,它将是一个表达式,并且无效。需要明确的是,这与你是否在全球环境中无关。
  • 这:“操作符 = 只允许在顶层”是一个广泛存在的误解,完全错误。
  • 这不是真的 - 例如,这有效,即使赋值不是一个完整的表达式:1 + (x = 2)
  • 为了澄清 KonradRudolph 和 PavelMinaev 的 cmets,我认为说它完全错误太强了,但有一个例外,即“通过大括号或额外的一对括号。”
  • function() x = 1, repeat x = 1, if (TRUE) x = 1....
【解决方案7】:

这也可能有助于理解这两个运算符之间的区别:

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

【讨论】:

  • 您能否更详细地解释为什么会发生这种情况/这里发生了什么? (提示:data.frame 尝试使用提供的变量的名称作为数据框中元素的名称)
  • 只是想,这可能是一个错误吗?如果是这样,我应该如何以及在哪里报告?
  • 这不是错误。我试图在上面的评论中暗示答案。设置元素名称时,R 将使用 make.names("b &lt;- rnorm(10)") 的等价物。
【解决方案8】:

我不确定 Patrick Burns 的书 R i​​nferno 是否在此处被引用,其中 8.2.26 = 不是 的同义词,Patrick 声明“你显然不想使用 'https://www.burns-stat.com/documents/books/the-r-inferno/获取。

【讨论】:

  • 是的,it has been mentioned。但问题是关于赋值运算符,而您的摘录涉及传递参数的语法。应该明确说明(因为围绕这一点存在很大的混淆)这 不是 赋值运算符。
猜你喜欢
  • 2018-04-20
  • 1970-01-01
  • 2017-05-27
  • 2015-12-25
  • 1970-01-01
  • 2019-01-03
相关资源
最近更新 更多