R中的赋值运算符=
和<-
有什么区别?
我知道运营商略有不同,如本例所示
x <- y <- 5
x = y = 5
x = y <- 5
x <- y = 5
# Error in (x <- y) = 5 : could not find function "<-<-"
但这是唯一的区别吗?
当您使用它们在函数调用中设置参数值时,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
帮助页面所示。
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。
?
的优先级实际上正好在 =
和 <-
之间,这在覆盖 ?
时会产生重要后果,而在其他情况下几乎没有。
=
在构建解析树之前会得到特殊处理。可能与函数参数有关,在 foo(x = a ? b)
中我们会在解析表达式的其余部分之前查找 =
是有道理的。
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 个字符,而不仅仅是“<-
”。
TRUE
。因此,如果您打算测试 x
是否小于 -y
,您可能会编写不会发出警告或错误的 if (x<-y)
,并且看起来工作正常。不过,只有在 y=0
时才会是 FALSE
。
=
并使用 <-
,那么很难说不需要额外的 grep "[^<]<-[^ ]" *.R
步骤。 =
不需要这样的 grep
。
=
,为什么要用 <-
伤害您的眼睛和手指?在 99.99% 的情况下,=
都很好。但有时您需要 <<-
,这是不同的历史。
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
。
根据 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
值的无用代码,你是想这样写吗?”,但是,那可能是白日梦...
运算符 <-
和 =
分配到评估它们的环境中。运算符 <-
可以在任何地方使用,而运算符 =
只允许在顶层(例如,在命令提示符下键入的完整表达式中)或作为表达式括号列表中的子表达式之一。
x <- 42
本身就是一个声明;在 if (x <- 42) {}
中它将是一个表达式,并且无效。需要明确的是,这与你是否在全球环境中无关。
1 + (x = 2)
function() x = 1
、repeat x = 1
、if (TRUE) x = 1
....
这也可能增加对这两个运算符之间差异的理解:
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
尝试使用提供的变量的名称作为数据框中元素的名称)
make.names("b <- rnorm(10)")
的等价物。
我不确定Patrick Burns 的书R inferno 是否在8.2.26 = 不是<- 的同义词中被引用,Patrick 声明“你显然不想使用'<-'当你想设置函数的参数时。”。这本书在 https://www.burns-stat.com/documents/books/the-r-inferno/ 有售
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
。 函数声明执行所述声明。函数调用没有(使用命名的...
参数会变得更复杂一些)。