R中"="和"< - "之间有什么区别?

csg*_*pie 670 r assignment-operator r-faq

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

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

x <- y <- 5
x = y = 5
x = y <- 5
x <- y = 5
# Error in (x <- y) = 5 : could not find function "<-<-"
Run Code Online (Sandbox Code Playgroud)

但这是唯一的区别吗?

Ric*_*ton 647

当您使用它们在函数调用中设置参数值时,赋值运算符的差异会更明显.例如:

median(x = 1:10)
x   
## Error: object 'x' not found
Run Code Online (Sandbox Code Playgroud)

在这种情况下,x声明在函数的范围内,因此它不存在于用户工作空间中.

median(x <- 1:10)
x    
## [1]  1  2  3  4  5  6  7  8  9 10
Run Code Online (Sandbox Code Playgroud)

在这种情况下,x在用户工作区中声明,因此您可以在函数调用完成后使用它.


R社区普遍倾向于使用<-赋值(功能签名除外)与(非常)旧版本的S-Plus兼容.请注意,空格有助于澄清类似的情况

x<-3
# Does this mean assignment?
x <- 3
# Or less than?
x < -3
Run Code Online (Sandbox Code Playgroud)

大多数R IDE都有键盘快捷键,<-以便于输入. Ctrl+ =在Architect中,Alt+ -在RStudio(Option+ -在macOS下),Shift+ -(下划线)在emacs + ESS中.


如果你喜欢写=<-,但要使用公开发布的代码比较常见的赋值符号(CRAN上,例如),那么你可以使用的一个tidy_*功能,在formatR包自动替换=<-.

library(formatR)
tidy_source(text = "x=1:5", arrow = TRUE)
## x <- 1:5
Run Code Online (Sandbox Code Playgroud)

"为什么会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
Run Code Online (Sandbox Code Playgroud)

解析器解释x <- y <- 5

`<-`(x, `<-`(y, 5))
Run Code Online (Sandbox Code Playgroud)

我们可以预期,x <- y = 5那么将是

`<-`(x, `=`(y, 5))
Run Code Online (Sandbox Code Playgroud)

但实际上它被解释为

`=`(`<-`(x, y), 5)
Run Code Online (Sandbox Code Playgroud)

这是因为=优先级低于帮助页面<-上显示的优先级?Syntax.

  • 我刚刚意识到你对`x < - x = 5`如何解释的解释有点错误:实际上,R把它解释为```< - < - `(x,y = 5,value = 5)` `(它本身或多或少等同于``tmp < - x; x < - `< - < - `(tmp,y = 5,value = 5)``).哎呀! (5认同)
  • 这一点也在Patrick Burns的[The R Inferno](http://www.burns-stat.com/pages/Tutor/R_inferno.pdf)第8.2.26章中提到过(不是我,而是建议) (4认同)
  • 但是,`median((x = 1:10))`与`median(x < - 1:10)`具有相同的效果. (3认同)
  • …而且我刚刚意识到,这个答案的第一部分是错误的,并且很不幸,这是很容易引起误解的,因为它会造成一个普遍的误解:在函数调用中使用`=`的方式**不执行赋值**,并且不是赋值运算符。这是一个完全不同的已解析R表达式,恰好使用相同的字符。此外,您显示的代码不会在函数范围内“声明” x。功能声明执行所述声明。函数调用没有(使用命名的`...`参数会更复杂)。 (3认同)
  • 我真的不考虑它们的快捷方式,无论如何您按相同数量的键 (2认同)
  • @DryLabRebel该快捷方式的问题是,R的最受推荐的样式约定在定界符“ like_this”中使用下划线,并且使用此快捷方式键入非常令人讨厌。 (2认同)

Nos*_*dna 102

谷歌的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个字符,包括空格,而不仅仅是"< - ". (124认同)
  • 如果可以使用`=`,为什么用`<-`伤害你的眼睛和手指?在99.99%的时间里`=`很好.有时候你需要`<< - `,这是一个不同的历史. (30认同)
  • 请注意,任何非0都被R视为"TRUE".因此,如果您打算测试`x`是否小于`-y`,您可能会编写`if(x <-y)`,它不会发出警告或错误,似乎工作正常.但是,当`y = 0`时,它只会是'FALSE'. (12认同)
  • 对< - 的关注可能是缺少+ =和 - =的蹩脚原因之一. (10认同)
  • 如果你禁止`=`并使用`< - `那么很难说不需要额外的'grep"[^ <] < - [^]"*.R`步骤.`=`不需要这样的`grep`. (3认同)
  • 除了 0.01% 之外,它不起作用对于任何不只是 R 的人来说都是糟糕的、不可读的、hacky 的快捷代码。这就像通过移位进行半除法只是为了看起来聪明(但你不是)。我的观点是 R 应该弃用 `&lt;-` 运算符。 (2认同)

Kon*_*lph 49

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

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

…
‘-> ->>’           rightwards assignment
‘<- <<-’           assignment (right to left)
‘=’                assignment (right to left)
…
Run Code Online (Sandbox Code Playgroud)

但这是唯一的区别吗?

既然你在询问赋值运算符:是的,这是唯一的区别.但是,如果不相信你会被原谅.即使R文件?assignOps声称存在更多差异:

运算符<-可以在任何地方使用,而运算符=只允许在顶层(例如,在命令提示符下键入的完整表达式中)或作为支撑表达式列表中的子表达式之一.

我们不要过分夸大它:R文档(巧妙地)错误 [ 1 ].这很容易显示:我们只需要找到一个=运算符的反例,它不是(a)在顶层,也不是(b)在表达式(即{…; …})的支撑列表中的子表达式.- 无需再费周折:

x
# Error: object 'x' not found
sum((x = 1), 2)
# [1] 3
x
# [1] 1
Run Code Online (Sandbox Code Playgroud)

显然,我们在=上下文(a)和(b)之外执行了一项任务.那么,为什么几十年来核心R语言功能的文档出错呢?

这是因为在R的语法中,符号=有两个不同的含义,这些含义经常混淆:

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

让我们来看看.

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

‹function_name›(‹argname› = ‹value›, …)
‹function_name›(‹args›, ‹argname› = ‹value›, …)
Run Code Online (Sandbox Code Playgroud)

... =是定义命名参数传递的标记:它不是赋值运算符.此外,在某些句法语境中=完全被禁止:

if (‹var› = ‹value›) …
while (‹var› = ‹value›) …
for (‹var› = ‹value› in ‹value2›) …
for (‹var1› in ‹var2› = ‹value›) …
Run Code Online (Sandbox Code Playgroud)

其中任何一个都会在<bla>中引发错误"unexpected'='.

在任何其他上下文中,=指赋值运算符调用.特别是,仅在子表达式周围加上括号使得上述(a)中的任何一个有效,(b)赋值.例如,以下执行赋值:

median((x = 1 : 10))
Run Code Online (Sandbox Code Playgroud)

但是也:

if (! (nf = length(from))) return()
Run Code Online (Sandbox Code Playgroud)

现在你可能会反对这样的代码是残暴的(你可能是对的).但是我从base::file.copy函数中取代了这个代码(替换<-=) - 它在核心R代码库的大部分内容中都是普遍存在的模式.

John Chambers原始解释,R文档可能基于此,实际上解释了这一点:

[ =赋值]只允许在语法中的两个位置:在顶层(作为完整的程序或用户类型的表达式); 当与周围的逻辑结构隔离时,通过括号或一对额外的括号.


坦白:我早先撒了谎.还有在之间的一个额外的差异=<-经营者:他们调用不同的功能.默认情况下,这些函数执行相同的操作,但您可以单独覆盖它们中的任何一个以更改行为.相比之下,<-->(左到右分配),虽然语法不同,总是调用相同的功能.覆盖一个也会覆盖另一个.知道这一点很少实用,但它可以用于一些有趣的恶作剧.

  • @Moody_Mudskipper FWIW 这最终在 4.0.0 中得到修复。 (5认同)
  • 关于 R 文档中的优先级和错误,“?”的优先级实际上位于“=”和“&lt;-”之间,这在覆盖“?”时会产生重要的后果。`,除此之外几乎没有。 (3认同)
  • @Moody_Mudskipper 这太奇怪了!你似乎是对的,但根据*源代码* ([`main/gram.y`](https://github.com/wch/r-source/blob/386c3a93cbcaf95017fa6ae52453530fb95149f4/src/main/gram.y #L384-L390)),“?”的优先级已正确记录,并且低于“=”和“&lt;-”。 (2认同)
  • 我喜欢你对 R 语义的解释......我将其改写如下。“=”运算符已重载。其基本语义是将形式名称绑定到函数调用的 arglist 中的实际参数。在函数调用之外的大多数(但不是全部!)上下文中,它具有与“&lt;-”相同的语义:它将名称绑定到现有对象(或常量值),具有写时复制语义,具有如果当前未定义该名称,则定义此名称的副作用。在某些情况下,它必须使用 stop() 来警告天真的或粗心的用户,因为他们将其与“==”运算符混淆了。 (2认同)
  • @ClarkThomborson我不同意将其中一种含义称为“基本”语义,因为这意味着不存在的层次结构。而且我认为将 `=` 称为重载运算符也很令人困惑:R 中的术语“运算符”(至少在 R 4.0 之前!)具有特定含义,指的是具有特殊语法规则的函数调用。当用于将名称绑定到函数调用参数列表内的参数名称时,这不是“=”所做的事情。没有发生任何调用,因此在这种情况下“=”只是一个语法标记(如“;”),而不是运算符。 (2认同)
  • @ClarkThomborson无论您是否觉得它有帮助,这正是它的本质:*命名参数的语法“=”标记在解析阶段后完全消失,它根本不在解析树中表示,也不会对其进行评估。赋值中的名称绑定和函数调用中的名称绑定在 R 中发生根本不同(并且在不同程度上,在其他语言中),它不仅仅是“时间区别”,也不是由于运算符优先级所致。 (2认同)

Ste*_*ers 33

x = y = 5相当于x = (y = 5),因为赋值运算符"组"从右到左,有效.含义:分配5 y,留下数字5; 然后将该5分配给x.

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

当你混合不同类型的赋值运算符时,<-绑定比紧=.所以x = y <- 5被解释为x = (y <- 5),这是有道理的情况.

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

请参阅?Syntax?assignOps查看优先级(绑定)和分组规则.


Aar*_*ica 31

根据John Chambers的说法,操作员=只允许在"顶层",这意味着它不允许在控制结构中使用if,使下面的编程错误成为非法.

> if(x = 0) 1 else x
Error: syntax error
Run Code Online (Sandbox Code Playgroud)

正如他所写的那样,"在控制表达式中不允许新的赋值形式[=]避免了编程错误(例如上面的例子),这些错误更可能与运算符相等而不是其他S赋值."

你可以设法做到这一点,如果它"与周围的逻辑结构隔离,通过括号或一对额外的括号",那么if ((x = 0)) 1 else x就可以了.

请参阅http://developer.r-project.org/equalAssign.html

  • 啊,是的,我忽略了你说"编程错误".这会导致错误,这实际上是个好消息.并且有理由更喜欢`x = 0`作为赋值而不是`x <-0`! (14认同)
  • 这是一个常见的错误,`x == 0`几乎总是意味着. (11认同)
  • 是的,这很好,这会导致一个错误,虽然我画了一个关于什么更喜欢的不同的教训; 我选择尽可能少地使用`=`因为`=`和`==`看起来很相似. (7认同)
  • 我的意思是,一个*真正*有用的错误检查器会在那里抛出一个错误并说"你有无用的代码总会返回'else`值,你的意思是这样写吗?",但是,这可能是一个管道梦想... (3认同)
  • 这个例子的呈现方式对我来说太奇怪了.`if(x = 0)1 else x`抛出错误,帮助我找到并纠正错误.`if(x < - 1)1 else x`不会抛出错误并且非常令人困惑. (2认同)

Hai*_*vgi 24

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

  • 我认为"顶级"意味着在声明级别,而不是表达级别.所以`x < - 42`本身就是一个陈述; 在`if(x < - 42){}中,它将是一个表达式,并且无效.需要说明的是,这与您是否处于全球环境无关. (8认同)

Den*_*lev 6

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

df <- data.frame(
      a = rnorm(10),
      b <- rnorm(10)
)
Run Code Online (Sandbox Code Playgroud)

对于第一个元素,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 ...
Run Code Online (Sandbox Code Playgroud)

R版本3.3.2(2016-10-31); macOS Sierra 10.12.1

  • 你能不能更详细地解释为什么会发生这种情况/这里发生了什么?(提示:`data.frame`尝试使用提供的变量的名称作为数据框中元素的名称) (6认同)
  • 这不是一个错误.我试图在上面的评论中暗示答案.设置元素的名称时,R将使用等效的`make.names("b < - rnorm(10)")`. (6认同)