为什么是 enquo + !!优于替代 + 评估 [英] Why is enquo + !! preferable to substitute + eval

查看:13
本文介绍了为什么是 enquo + !!优于替代 + 评估的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的例子中,为什么我们更喜欢使用 f1 而不是 f2?它在某种意义上更有效吗?对于习惯使用 R 的人来说,使用substitute + eval"选项似乎更自然.

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 NSE 函数和像替代 + eval 这样的好 ol' 基础 R 来编程,还是我真的需要学习喜欢所有那些 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 的答案,因为使用 enquo<有一个非常明显的优势/code> 超过 substitute.两者都查看函数的调用环境以识别赋予该函数的表达式.不同之处在于 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() 的简单函数:

Consider a simple function that uses 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 引入了一个新的运算符 {{(发音为curly curly"),它实际上是一个简写对于 !!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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆