使用相同的函数,但是使用名称%>%时会导致与使用名称:=时不同的结果。

4

我正在使用来自@Konrad Rudolph的一个函数,但将其名称从%>%更改为:=,并且在相同的调用中获得不同的结果。

`%>%` = function (lhs, rhs) {
  subst = call('substitute', substitute(rhs), list(. = lhs))
  eval.parent(eval(subst))
}

`:=` <- `%>%`

1 %>% .+2 %>% .*3
#[1] 7

1 := .+2 := .*3
#[1] 3

或者使用不同的函数。

`%>%` <- function(lhs, rhs) {
  assign(".", lhs, envir=parent.frame())
  eval(substitute(rhs), parent.frame())
}

`:=` <- `%>%`

1 %>% .+2 %>% .*3
#[1] 7

1 := .+2 := .*3
#[1] 9

为什么我使用名为:=的函数时会得到其他结果?
1个回答

7

原因是运算符优先级。我们可以使用 lobstr 包来查看代码的抽象语法树。

lobstr::ast(1 %>% .+1 %>% .+2)
█─`+` 
├─█─`+` 
│ ├─█─`%>%` 
│ │ ├─1 
│ │ └─. 
│ └─█─`%>%` 
│   ├─1 
│   └─. 
└─2 

vs

lobstr::ast(1 := .+1 := .+2)
█─`:=` 
├─1 
└─█─`:=` 
  ├─█─`+` 
  │ ├─. 
  │ └─1 
  └─█─`+` 
    ├─. 
    └─2 

所以当你运行%>%版本时,管道会在加法之前执行,但是使用:=版本,加法会在:=之前执行。

如果你加入隐式括号,你会看到这两个表达式是等价的。

1 %>% .+1 %>% .+2
((1 %>% .)+(1 %>% .))+2

然而,您可能期望的行为需要看起来像这样

1 %>% (.+1) %>% (.+2)

但仍然“有效”。这就是另一个表达式的失效原因

1 := .+1 := .+2
1 := ((.+1) := (.+2))
(1+1) := (1+2)

函数的定义方式,由于外部的:=替换了中间的.,因此内部的=没有剩余自由的.变量。

操作顺序在?Syntax帮助页面上有定义。您不能更改函数的优先级,除非更改R源代码本身。虽然在页面上没有明确列出,但:=<-具有相同的优先级(它在解析器中被别名为LEFT_ASSIGN)。


这对我来说还是有点困惑。:= <- %>% 如何不完全替代data.table::``:=`` 函数的任何行为? - Carl Witthoft
继续 - 因此,在查看实际函数定义之前,R优先级“解析”会发生吗?如果我创建任何带有“:”的名称的函数,这种情况都会发生吗? - Carl Witthoft
1
data.table 包定义了自己的 := 运算符,不在全局范围内。由于它们使用非标准评估,它们可以在特殊环境中评估表达式,以便永远不会使用全局 := - MrFlick
1
@CarlWitthoft 对于第二点,是的,解析会发生,并且抽象语法树会被创建,而不考虑函数的实际作用。符号本身决定了优先级。如果您定义了一个名为“:”的函数,则它将获得与本机“:”相同的优先级。但是没有规则允许您创建任何带有“:”名称的函数。 “:=”是R解析器中非常特殊的情况。它与您自己定义的“%any%”类型运算符不同。 - MrFlick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接