在以下示例中,为什么我们比f1
更喜欢使用f2
?从某种意义上说它更有效率吗?对于以前以R为基础的用户,使用“替换+评估”选项似乎更为自然。
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
换句话说,除了这个特定的示例之外,我的问题是:我可以摆脱使用具有良好基础R的
dplyr
NSE函数进行编程的麻烦,例如replace + eval,还是真的需要学习爱上所有这些rlang
函数,因为它有好处吗(速度,清晰度,组成性...)? 最佳答案
我想给出一个独立于dplyr
的答案,因为与enquo
相比,使用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函数。更新:
rlang 0.4.0
引入了一个新的运算符{{
(发音为“curly curl”),实际上是!!enquo()
的简写形式。这使我们可以将g2
的定义简化为g2 <- function( myExpr ) {
val <- f2( {{myExpr}} )
val
}
关于r - 为什么是enquo + !!最好替代+评估,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49700912/