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


712

赋值运算符=<-R 之间有什么区别?

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

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

但这是唯一的区别吗?


45
如此所述,该<-符号的来源来自旧的APL键盘,实际上<-它们上面只有一个键。
joran 2014年

Answers:


95

赋值运算符=<-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的语法中,符号=具有两种通常会被混淆的含义:

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

让我们来看看。

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

‹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所做原始解释,它实际上正确地解释了这一点:

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


告白:我早些说谎。还有在之间的一个额外的差异=<-经营者:他们调用不同的功能。默认情况下,这些功能执行相同的操作,但是您可以分别覆盖其中的任何一个以更改行为。相比之下,<-->(从左到右分配)尽管在语法上是不同的,但始终调用相同的函数。覆盖一个也覆盖另一个。知道这一点很少是实用的,可以用于一些有趣的恶作剧


1
关于R的文档中的优先级和错误,的优先级?实际上恰好在=和之间<-,这在覆盖时会产生重要的影响? ,而实际上没有其他影响。
Moody_Mudskipper

@Moody_Mudskipper太奇怪了!您似乎是对的,但是根据源代码main/gram.y),?正确记录了的优先级,并且优先级低于=<-
Konrad Rudolph

我不会讲C,但是我想=在解析树构建之前要进行特殊处理。也许与函数参数有关foo(x = a ? b)=在解析表达式的其余部分之前我们先寻找一下是有意义的。
Moody_Mudskipper

@Moody_Mudskipper 我问过r-devel
Konrad Rudolph

2
@Moody_Mudskipper FWIW最终在4.0.0中修复。
Konrad Rudolph

661

当您使用赋值运算符在函数调用中设置参数值时,它们之间的区别更加明显。例如:

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帮助页面上所示。


4
帕特里克·伯恩斯(Patrick Burns)的《 R地狱》The R Inferno)第8.2.26章也提到了这一点(不是我而是一个建议)
Uwe

3
但是,median((x = 1:10))具有与相同的效果median(x <- 1:10)
Francesco Napolitano

2
我并不真正考虑它们的快捷方式,无论如何您按相同数量的键
yosemite_k

5
我刚刚意识到,您对如何x <- x = 5解释的解释有些错误:实际上,R将其解释为​`<-<-`(x, y = 5, value = 5)(其本身或多或少等同于tmp <- x; x <- `<-<-`(tmp, y = 5, value = 5))。kes!
康拉德·鲁道夫'18

4
…而且我刚刚意识到,答案的第一部分是不正确的,而且很不幸,这是很容易引起误解的,因为它会造成一个普遍的误解:=在函数调用使用的方式不会执行赋值,而是一个赋值运算符。这是一个完全不同的已解析R表达式,恰好使用相同的字符。此外,您显示的代码不会x在函数范围内“声明” 。该函数声明执行该声明。函数调用没有(使用命名...参数会更加复杂)。
康拉德·鲁道夫

103

Google的R风格指南通过禁止分配“ =”来简化此问题。不错的选择。

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

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

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


133
意外的分配由下行x<-yx < -y是指,vexes我这么多,我个人比较喜欢=。让您的代码依赖于空白对我来说似乎不好。可以建议使用空格作为样式建议,但是无论空间是否存在,代码都会以不同的方式运行?如果重新格式化代码,或者使用搜索和替换,空格有时会消失并且代码出错了,该怎么办。这不是问题=。IIUC,禁止=等同于要求“ <- ”;即3个字符,包括一个空格,而不仅仅是“ <-”。
Matt Dowle 2012年

12
请注意,TRUER 会考虑任何非0的值。因此,如果要测试if x小于-y,则可能会编写if (x<-y)不会警告或错误的消息,并且看起来工作正常。不过只会是FALSEwhen y=0
Matt Dowle 2012年

4
如果您确实禁止=使用,<- 那么很难说grep "[^<]<-[^ ]" *.R不需要额外的步骤。=不需要这样的grep
Matt Dowle 2012年

34
<-如果可以使用,为什么会伤害眼睛和手指=?在99.99%的时间=里可以。有时您需要<<-,但这是不同的历史。
费尔南多

10
关注<-也许是缺少+ =和-=的。脚原因之一。
克里斯

37

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

这与并不相同(x = y) = 5,这是行不通的!含义:分配yto x的值,保留to 的值y;然后将5分配给嗯...到底是什么?

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

不幸的是,x <- y = 5被解释为(x <- y) = 5,这是行不通的!

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


是的,正如Konrad Rudolph的回答 在优先级表的<- <<-上方=,这意味着<-将首先执行。因此,x <- y = 5应以方式执行(x <- y) = 5
尼克·东(Dong Dong)

1
@Nick Dong是的。有用的是,运算符优先级表在?Syntax {base}中明确记录。
史蒂夫·投手

33

根据约翰·钱伯斯(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


11
这是一个常见的错误,x==0几乎总是意味着错误。
亚伦(Aaron)

14
嗯,是的,我忽略了您说的“编程错误”。引起错误实际上是一个好消息。并且有一个很好的理由更喜欢x=0作为任务x<-0
史蒂夫·投手

7
是的,这会导致错误,这很不错,尽管我对喜欢的东西有不同的看法。我选择使用=尽可能少的,因为===看起来非常类似。
亚伦(Aaron)

2
这个例子的呈现方式对我来说很奇怪。if(x = 0) 1 else x引发错误,帮助我发现并纠正错误。if(x <- 1) 1 else x不会引发错误并且非常令人困惑。
Gregor Thomas

3
我的意思是,一个非常有用的错误检查器会在那里抛出一个错误,并说“您有无用的代码将始终返回该else值,您是要用这种方式编写它吗?”,但是,这可能是个白日梦……
TylerH

26

操作员<-并将其=分配到评估他们的环境中。运算符<-可以在任何地方使用,而运算符=只能在最高级别(例如,在命令提示符下键入的完整表达式中)使用,也可以作为括号中的子表达式之一使用。


8
我认为“顶层”是指语句级别,而不是表达级别。因此x <- 42,它本身就是一个声明;在if (x <- 42) {}这将是一个表达,是无效的。需要明确的是,这与您是否处于全球环境无关。
史蒂夫·投手

1
这就是:“运算符=仅在最高级别被允许”是一种普遍存在的误解,完全是错误的。
Konrad Rudolph'3

这是不正确的-例如,即使分配不是一个完整的表达式,它也可以工作:1 + (x = 2)
Pavel Minaev

1
为了弄清KonradRudolph和PavelMinaev的评论,我认为说这完全是错是太过分了,但是有一个例外,那就是它是“通过括号或额外的一对括号与周围的逻辑结构隔离开”。
亚伦离开堆栈溢出

或者function() x = 1repeat x = 1if (TRUE) x = 1...
Moody_Mudskipper

6

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

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


6
您能否详细解释为什么发生这种情况/这里发生了什么?(提示:data.frame尝试使用提供的变量的名称作为数据框中元素的名称)
Ben Bolker 2016年

只是认为,这可能是一个错误吗?如果是这样,我如何以及在哪里报告?
丹尼斯·拉苏列夫

7
这不是错误。我试图在上面的评论中暗示答案。设置元素名称时,R将使用等价于make.names("b <- rnorm(10)")
本·博克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.