在下面的例子中,为什么我们应该赞成使用f1
over f2
?从某种意义上说,它是否更高效?对于习惯使用 R 基础的人来说,使用“substitute + eval”选项似乎更自然。
library(dplyr)
d = data.frame(x = 1:5,
y = rnorm(5))
# using enquo + !!
f1 = function(mydata, myvar) {
m = enquo(myvar)
mydata %>%
mutate(two_y = 2 * !!m)
}
# using substitute + eval
f2 = function(mydata, myvar) {
m = substitute(myvar)
mydata %>%
mutate(two_y = 2 * eval(m))
}
all.equal(d %>% f1(y), d %>% f2(y)) # TRUE
换句话说,除了这个特定的例子之外,我的问题是:我可以使用dplyr
NSE 具有良好的基础 R 功能,例如替代+评估,或者我真的需要学会喜欢所有这些rlang
之所以起作用,是因为它有好处(速度、清晰度、组合性……)?
我想给出一个独立的答案dplyr
,因为使用有一个非常明显的优势enquo
over substitute
。两者都在函数的调用环境中查找以识别赋予该函数的表达式。不同之处在于substitute()
只执行一次,同时!!enquo()
将正确地遍历整个调用堆栈。
考虑一个简单的函数,它使用substitute()
:
f <- function( myExpr ) {
eval( substitute(myExpr), list(a=2, b=3) )
}
f(a+b) # 5
f(a*b) # 6
当调用嵌套在另一个函数中时,此功能会中断:
g <- function( myExpr ) {
val <- f( substitute(myExpr) )
## Do some stuff
val
}
g(a+b)
# myExpr <-- OOPS
现在考虑使用重写相同的函数enquo()
:
library( rlang )
f2 <- function( myExpr ) {
eval_tidy( enquo(myExpr), list(a=2, b=3) )
}
g2 <- function( myExpr ) {
val <- f2( !!enquo(myExpr) )
val
}
g2( a+b ) # 5
g2( b/a ) # 1.5
这就是为什么enquo()
+ !!
优于substitute()
+ eval()
. dplyr
只是充分利用这一特性来构建一组连贯的 NSE 函数。
UPDATE: rlang 0.4.0
引入了一个新的运算符{{
(发音为“curly curly”),这实际上是!!enquo()
。这使我们能够简化定义g2
to
g2 <- function( myExpr ) {
val <- f2( {{myExpr}} )
val
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)