rlang::enexpr 和 base::substitute 之间的真正区别
True difference between rlang::enexpr and base::substitute
此问题与 发布的问题相关。
简而言之,我正在寻找为什么 base::substitute
和 rlang::enexpr
在下面表现不同。
#works
f1 <- function(x,y){
do.call("methods", list(substitute(x::y)))
}
f1(broom,tidy)
#does not work
#Error: `arg` must be a symbol
f2 <- function(x,y){
do.call('methods',list(rlang::enexpr(x::y)))
}
f2(broom,tidy)
较长的版本。在高级 R 书 chapter 19 中,您可以看到表 19.1 和 19.2 建议 enexpr
和 substitute
应该在函数调用中服务于相同的目的(我在这里可能是错的并且会很高兴解释我为什么错了)。
我决定对此进行测试,并在 f1
returns 结果中看到了这一点,但 f2
returns 出现了错误。
有趣的是,如果您使用 do.call('methods',list(rlang::expr(broom::tidy)))
,这会起作用。我觉得这很有趣,因为 rlang::expr
只是调用 rlang::enexpr
。
在上面的问题中,MrFlick 将此功能发布为纯 rlang 解决方案
#also works
#function from MrFlick in the posted link
f3 <- function(x,y){
x <- rlang::ensym(x)
y <- rlang::ensym(y)
rlang::eval_tidy(rlang::quo(methods(`::`(!!x, !!y))))}
f3(broom,tidy)
这似乎比我预期的要复杂一些。
知道为什么 f1
和 f2
不等价或如何使 f2
与 enexpr
一起工作会很有帮助。
rlang::enexpr()
和 base::substitute()
在它们的界面上并不完全相同。 enexpr()
需要一个引用函数输入参数之一的变量名称,而 substitute()
可以使用任意表达式。这需要额外的表达式算法——通过用 rlang::expr()
包装并使用反引号运算符 !!
——将 enexpr()
的结果放入更复杂的表达式中:
g <- function(x) substitute(x+5)
h <- function(x) rlang::enexpr(x+5)
h2 <- function(x) rlang::expr( !!rlang::enexpr(x) + 5 )
g(a) # a + 5
h(a) # Error: `arg` must be a symbol
h2(a) # a + 5
要使 f2
起作用,您需要将 rlang::enexpr()
分别应用于每个参数,然后使用表达式算术来组成整个 x::y
表达式:
f2 <- function(x,y){
ee <- rlang::expr( `::`(!!rlang::enexpr(x), !!rlang::enexpr(y)) )
do.call('methods',list(ee))
}
f2(broom,tidy)
请注意,我们必须在前缀表示法中使用 ::
,因为 !!
和 ::
彼此相邻对于解析器来说是一个问题。换句话说,!!a :: !!b
类型的表达式会导致解析错误。
另一种方法是让用户自己编写表达式。这样你就可以摆脱一个 rlang::enexpr()
:
f3 <- function(x)
do.call('methods',list(rlang::enexpr(x)))
f3( broom::tidy )
旁注:
尽管 expr()
调用了 enexpr()
,但该调用是针对 expr()
本身的范围。考虑一下,
f1 <- function(x) rlang::enexpr(x)
f2 <- function(x) {
g <- function(y) rlang::enexpr(y)
g(x)
}
两者不等价,因为第二个enexpr()
作用域为内部函数g()
,而不是外部f2()
.
f1(abc) # abc
f2(abc) # x
此问题与
简而言之,我正在寻找为什么 base::substitute
和 rlang::enexpr
在下面表现不同。
#works
f1 <- function(x,y){
do.call("methods", list(substitute(x::y)))
}
f1(broom,tidy)
#does not work
#Error: `arg` must be a symbol
f2 <- function(x,y){
do.call('methods',list(rlang::enexpr(x::y)))
}
f2(broom,tidy)
较长的版本。在高级 R 书 chapter 19 中,您可以看到表 19.1 和 19.2 建议 enexpr
和 substitute
应该在函数调用中服务于相同的目的(我在这里可能是错的并且会很高兴解释我为什么错了)。
我决定对此进行测试,并在 f1
returns 结果中看到了这一点,但 f2
returns 出现了错误。
有趣的是,如果您使用 do.call('methods',list(rlang::expr(broom::tidy)))
,这会起作用。我觉得这很有趣,因为 rlang::expr
只是调用 rlang::enexpr
。
在上面的问题中,MrFlick 将此功能发布为纯 rlang 解决方案
#also works
#function from MrFlick in the posted link
f3 <- function(x,y){
x <- rlang::ensym(x)
y <- rlang::ensym(y)
rlang::eval_tidy(rlang::quo(methods(`::`(!!x, !!y))))}
f3(broom,tidy)
这似乎比我预期的要复杂一些。
知道为什么 f1
和 f2
不等价或如何使 f2
与 enexpr
一起工作会很有帮助。
rlang::enexpr()
和 base::substitute()
在它们的界面上并不完全相同。 enexpr()
需要一个引用函数输入参数之一的变量名称,而 substitute()
可以使用任意表达式。这需要额外的表达式算法——通过用 rlang::expr()
包装并使用反引号运算符 !!
——将 enexpr()
的结果放入更复杂的表达式中:
g <- function(x) substitute(x+5)
h <- function(x) rlang::enexpr(x+5)
h2 <- function(x) rlang::expr( !!rlang::enexpr(x) + 5 )
g(a) # a + 5
h(a) # Error: `arg` must be a symbol
h2(a) # a + 5
要使 f2
起作用,您需要将 rlang::enexpr()
分别应用于每个参数,然后使用表达式算术来组成整个 x::y
表达式:
f2 <- function(x,y){
ee <- rlang::expr( `::`(!!rlang::enexpr(x), !!rlang::enexpr(y)) )
do.call('methods',list(ee))
}
f2(broom,tidy)
请注意,我们必须在前缀表示法中使用 ::
,因为 !!
和 ::
彼此相邻对于解析器来说是一个问题。换句话说,!!a :: !!b
类型的表达式会导致解析错误。
另一种方法是让用户自己编写表达式。这样你就可以摆脱一个 rlang::enexpr()
:
f3 <- function(x)
do.call('methods',list(rlang::enexpr(x)))
f3( broom::tidy )
旁注:
尽管 expr()
调用了 enexpr()
,但该调用是针对 expr()
本身的范围。考虑一下,
f1 <- function(x) rlang::enexpr(x)
f2 <- function(x) {
g <- function(y) rlang::enexpr(y)
g(x)
}
两者不等价,因为第二个enexpr()
作用域为内部函数g()
,而不是外部f2()
.
f1(abc) # abc
f2(abc) # x