ChatGPT解决这个技术问题 Extra ChatGPT

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

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

我知道运营商略有不同,如本例所示

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

但这是唯一的区别吗?

正如 here 所指出的,<- 符号的起源来自实际上只有一个 <- 键的旧 APL 键盘。

R
Richie Cotton

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

R 社区普遍倾向于使用 <- 进行赋值(函数签名除外),以便与(非常)旧版本的 S-Plus 兼容。请注意,空格有助于澄清情况,例如

x<-3
# Does this mean assignment?
x <- 3
# Or less than?
x < -3

大多数 R IDE 都有键盘快捷键,使 <- 更易于键入。 Ctrl + = 在 Architect 中,Alt + - 在 RStudio 中(Option + < macOS 下的 kbd>-),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 的语法包含必须以一种或另一种方式解析的 many ambiguous cases。解析器根据使用的是 = 还是 <- 选择以不同的顺序解析表达式的位。

要了解发生了什么,您需要知道赋值以静默方式返回所分配的值。您可以通过显式打印更清楚地看到这一点,例如 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 帮助页面所示。


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

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

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

… '-> ->>' 向右赋值 '<- <<-' 赋值(从右到左) '=' 赋值(从右到左)…

但这是唯一的区别吗?

由于您询问的是 赋值运算符:是的,这是唯一的区别。但是,如果您不相信,您会被原谅。甚至 ?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›, …)

… = 是定义命名参数传递的记号:它是 not 赋值运算符。此外,在某些句法上下文中,= 完全禁止

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 实际上正确地解释了这一点:

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

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


关于 R 文档中的优先级和错误,? 的优先级实际上正好在 =<- 之间,这在覆盖 ? 时会产生重要后果,而在其他情况下几乎没有。
@Moody_Mudskipper 这很奇怪!您似乎是对的,但根据 源代码 (main/gram.y),? 的优先级已正确记录,并且低于 =<-
我不会说 C,但我认为 = 在构建解析树之前会得到特殊处理。可能与函数参数有关,在 foo(x = a ? b) 中我们会在解析表达式的其余部分之前查找 = 是有道理的。
@Moody_Mudskipper I’ve asked r-devel
@Moody_Mudskipper FWIW 这最终在 4.0.0 中得到修复。
x
xxfelixxx

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

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

这与 (x = y) = 5 不同,后者不起作用!含义:将y的值赋给x,留下y的值;然后将 5 分配给,嗯...,到底是什么?

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

不幸的是,x <- y = 5 被解释为 (x <- y) = 5,这种情况不起作用!

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


是的,正如 Konrad Rudolph 的回答所说,<- <<- 在优先表中高于 =,这意味着将首先执行 <-。因此,x <- y = 5 应作为 (x <- y) = 5 执行。
@Nick Dong 确实是的。有用的是,运算符优先级表在 ?Syntax {base} 中明确记录。
A
Aaron left Stack Overflow

根据 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<-0
是的,很高兴这会导致错误,尽管我吸取了不同的教训来选择更喜欢什么;我选择尽可能少地使用 =,因为 === 看起来非常相似。
这个例子的呈现方式对我来说太奇怪了。 if(x = 0) 1 else x 引发错误,帮助我找到并纠正错误。 if(x <- 1) 1 else x 不会抛出错误并且非常混乱。
我的意思是,一个真正有用的错误检查器会在那里抛出一个错误并说“你有总是返回 else 值的无用代码,你是想这样写吗?”,但是,那可能是白日梦...
n
nbro

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


我认为“顶级”是指语句级别,而不是表达式级别。所以 x <- 42 本身就是一个声明;在 if (x <- 42) {} 中它将是一个表达式,并且无效。需要明确的是,这与你是否在全球环境中无关。
这:“操作符 = 只允许在顶层”是一个广泛存在的误解,并且是完全错误的。
这不是真的 - 例如,这有效,即使赋值不是一个完整的表达式:1 + (x = 2)
为了澄清 KonradRudolph 和 PavelMinaev 的评论,我认为说它完全错误是太过分了,但是有一个例外,那就是它“通过大括号或一对额外的括号与周围的逻辑结构隔离开来”。
或在 function() x = 1repeat x = 1if (TRUE) x = 1....
S
Scarabee

这也可能增加对这两个运算符之间差异的理解:

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 <- rnorm(10)") 的等价物。
D
Diego

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


是的,it has been mentioned。但问题是关于赋值运算符,而您的摘录涉及传递参数的语法。应该明确说明(因为围绕这一点存在很大的混淆)这是 不是 赋值运算符。