enquo + !! は、substitute + eval よりもなぜ優れているのでしょうか? 質問する

enquo + !! は、substitute + eval よりもなぜ優れているのでしょうか? 質問する

次の例では、f1よりもを使用する方がよいのはなぜでしょう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

言い換えれば、そしてこの特定の例を超えて、私の質問は、dplyrsubstitute+eval のような古き良き基本 R で NSE 関数を使用してプログラミングすることで済むのか、それとも、速度、明瞭性、構成性などの利点があるため、それらの関数をすべて愛用することを学ぶ必要があるのかrlang​​ということです。

ベストアンサー1

よりもdplyrを使用する方が明らかに有利なので、とは独立した回答をしたいと思います。 は両方とも関数の呼び出し環境を調べて、その関数に与えられた式を識別します。 違いは、 はこれを 1 回だけ行うのに対し、は呼び出しスタック全体を正しくたどることです。enquosubstitutesubstitute()!!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()+が+!!よりも好ましい理由です。は、この特性を最大限に活用して、一貫した NSE 関数のセットを構築します。substitute()eval()dplyr

アップデート: rlang 0.4.0は新しい演算子{{(「カーリーカーリー」と発音)を導入しました。これは実質的に の省略形です。これによりの!!enquo()定義を簡略化できます。g2

g2 <- function( myExpr ) {
  val <- f2( {{myExpr}} )
  val
}

おすすめ記事