为什么是enquo + !!最好替代+评估 [英] Why is enquo + !! preferable to substitute + eval
问题描述
在下面的示例中,为什么我们应该优先使用f1
而不是f2
?从某种意义上说它更有效率吗?对于曾经以R为基础的用户,使用替换+评估"选项似乎更为自然.
In the following example, why should we favour using f1
over f2
? Is it more efficient in some sense? For someone used to base R, it seems more natural to use the "substitute + eval" option.
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
具有良好ol'base R的NSE函数进行编程的麻烦,例如replace + eval,还是我真的需要学习爱所有这些rlang
函数,是因为它有好处(速度,清晰度,组成性...)?
In other words, and beyond this particular example, my question is: can I get get away with programming using dplyr
NSE functions with good ol' base R like substitute+eval, or do I really need to learn to love all those rlang
functions because there is a benefit to it (speed, clarity, compositionality,...)?
推荐答案
我想给出一个独立于dplyr
的答案,因为与substitute
相比,使用enquo
有一个非常明显的优势.两者都在函数的调用环境中进行查找,以识别提供给该函数的表达式.区别在于substitute()
仅执行一次,而!!enquo()
将正确地遍历整个调用堆栈.
I want to give an answer that is independent of dplyr
, because there is a very clear advantage to using enquo
over substitute
. Both look in the calling environment of a function to identify the expression that was given to that function. The difference is that substitute()
does it only once, while !!enquo()
will correctly walk up the entire calling stack.
考虑使用substitute()
的简单函数:
f <- function( myExpr ) {
eval( substitute(myExpr), list(a=2, b=3) )
}
f(a+b) # 5
f(a*b) # 6
当调用嵌套在另一个函数中时,此功能将中断:
This functionality breaks when the call is nested inside another function:
g <- function( myExpr ) {
val <- f( substitute(myExpr) )
## Do some stuff
val
}
g(a+b)
# myExpr <-- OOPS
现在考虑使用enquo()
重写的相同功能:
Now consider the same functions re-written using 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函数.
And that is why enquo()
+ !!
is preferable to substitute()
+ eval()
. dplyr
simply takes full advantage of this property to build a coherent set of NSE functions.
更新:rlang 0.4.0
引入了新的运算符{{
(读作卷曲"),实际上是!!enquo()
的简写形式.这使我们可以简化g2
至
UPDATE: rlang 0.4.0
introduced a new operator {{
(pronounced "curly curly"), which is effectively a short hand for !!enquo()
. This allows us to simplify the definition of g2
to
g2 <- function( myExpr ) {
val <- f2( {{myExpr}} )
val
}
这篇关于为什么是enquo + !!最好替代+评估的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!