次の例では、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
言い換えれば、そしてこの特定の例を超えて、私の質問は、dplyr
substitute+eval のような古き良き基本 R で NSE 関数を使用してプログラミングすることで済むのか、それとも、速度、明瞭性、構成性などの利点があるため、それらの関数をすべて愛用することを学ぶ必要があるのかrlang
ということです。
ベストアンサー1
よりもdplyr
を使用する方が明らかに有利なので、とは独立した回答をしたいと思います。 は両方とも関数の呼び出し環境を調べて、その関数に与えられた式を識別します。 違いは、 はこれを 1 回だけ行うのに対し、は呼び出しスタック全体を正しくたどることです。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()
+が+!!
よりも好ましい理由です。は、この特性を最大限に活用して、一貫した NSE 関数のセットを構築します。substitute()
eval()
dplyr
アップデート: rlang 0.4.0
は新しい演算子{{
(「カーリーカーリー」と発音)を導入しました。これは実質的に の省略形です。これによりの!!enquo()
定義を簡略化できます。g2
g2 <- function( myExpr ) {
val <- f2( {{myExpr}} )
val
}