赋值运算符=
和<-
R 之间有什么区别?
我知道运算符略有不同,如本例所示
x <- y <- 5
x = y = 5
x = y <- 5
x <- y = 5
# Error in (x <- y) = 5 : could not find function "<-<-"
但这是唯一的区别吗?
赋值运算符=
和<-
R 之间有什么区别?
我知道运算符略有不同,如本例所示
x <- y <- 5
x = y = 5
x = y <- 5
x <- y = 5
# Error in (x <- y) = 5 : could not find function "<-<-"
但这是唯一的区别吗?
Answers:
赋值运算符
=
和<-
R 之间有什么区别?
如您的示例所示,=
并且<-
运算符优先级略有不同(确定它们在同一表达式中混合时的求值顺序)。实际上,?Syntax
在R中给出了以下运算符优先级表,从最高到最低:
… ‘-> ->>’ rightwards assignment ‘<- <<-’ assignment (right to left) ‘=’ assignment (right to left) …
但这是唯一的区别吗?
由于您正在询问赋值运算符:是的,那是唯一的区别。但是,您会相信其他理由。即使是R文档,也?assignOps
声称存在更多差异:
运算符
<-
可以在任何地方使用,而运算符=
只能在最高级别(例如,在命令提示符下键入的完整表达式中)使用,也可以作为括号中的子表达式之一使用。
我们不要对此提出过分的观点:R文档是( 有点)错误的[ 1 ]。这很容易表明:我们只需要找到一个=
运算符的反例,它不是(a)在顶层,也不是(b)在括号列表(即{…; …}
)中的子表达式。- 无需再费周折:
x
# Error: object 'x' not found
sum((x = 1), 2)
# [1] 3
x
# [1] 1
显然,我们在=
上下文(a)和(b)之外使用进行了赋值。那么,为什么几十年来错误的核心R语言功能文档呢?
这是因为在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)赋值有效。例如,以下执行分配:
median((x = 1 : 10))
但是也:
if (! (nf = length(from))) return()
现在您可能会反对这样的代码是残酷的(您可能是正确的)。但是我把这个代码的base::file.copy
函数(替换<-
用=
) -这是在多核心- [R代码库的普遍模式。
R文档可能基于John Chambers所做的原始解释,它实际上正确地解释了这一点:
[
=
赋值]仅在语法的两个位置允许:顶级(作为完整程序或用户键入的表达式);当与周围的逻辑结构隔离时,可以使用大括号或一对额外的括号。
告白:我早些说谎。还有是在之间的一个额外的差异=
和<-
经营者:他们调用不同的功能。默认情况下,这些功能执行相同的操作,但是您可以分别覆盖其中的任何一个以更改行为。相比之下,<-
和->
(从左到右分配)尽管在语法上是不同的,但始终调用相同的函数。覆盖一个也覆盖另一个。知道这一点很少是实用的,但可以用于一些有趣的恶作剧。
?
实际上恰好在=
和之间<-
,这在覆盖时会产生重要的影响?
,而实际上没有其他影响。
=
在解析树构建之前要进行特殊处理。也许与函数参数有关foo(x = a ? b)
,=
在解析表达式的其余部分之前我们先寻找一下是有意义的。
当您使用赋值运算符在函数调用中设置参数值时,它们之间的区别更加明显。例如:
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下),在emacs + ESS中Shift+ -(下划线)。
如果您更喜欢写信=
,<-
但想对公开发布的代码使用更通用的赋值符号(例如,在CRAN上),则可以使用包中的tidy_*
功能之一formatR
自动替换=
为<-
。
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
帮助页面上所示。
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)
)。kes!
=
在函数调用中使用的方式不会执行赋值,而是一个赋值运算符。这是一个完全不同的已解析R表达式,恰好使用相同的字符。此外,您显示的代码不会x
在函数范围内“声明” 。该函数声明执行该声明。函数调用没有(使用命名...
参数会更加复杂)。
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
是指,vexes我这么多,我个人比较喜欢=
。让您的代码依赖于空白对我来说似乎不好。可以建议使用空格作为样式建议,但是无论空间是否存在,代码都会以不同的方式运行?如果重新格式化代码,或者使用搜索和替换,空格有时会消失并且代码出错了,该怎么办。这不是问题=
。IIUC,禁止=
等同于要求“ <-
”;即3个字符,包括一个空格,而不仅仅是“ <-
”。
TRUE
R 会考虑任何非0的值。因此,如果要测试if x
小于-y
,则可能会编写if (x<-y)
不会警告或错误的消息,并且看起来工作正常。不过只会是FALSE
when y=0
。
=
使用,<-
那么很难说grep "[^<]<-[^ ]" *.R
不需要额外的步骤。=
不需要这样的grep
。
<-
如果可以使用,为什么会伤害眼睛和手指=
?在99.99%的时间=
里可以。有时您需要<<-
,但这是不同的历史。
x = y = 5
等价于x = (y = 5)
,因为赋值运算符从右到左“分组”,这是可行的。含义:将5分配给y
,保留数字5;然后将5分配给x
。
这与并不相同(x = y) = 5
,这是行不通的!含义:分配y
to x
的值,保留to 的值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
。
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)")
。
<-
符号的来源来自旧的APL键盘,实际上<-
它们上面只有一个键。