r语言 - rlang::enexpr 和 base::substitute 之间的真实区别



这个问题与这里发布的问题有关。

简而言之,我正在寻找为什么base::substituterlang::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 书籍第 19 章中,您可以看到表 19.1 和 19.2 建议enexprsubstitute在函数调用中应该具有相同的目的(我在这里可能是错的,并且很乐意解释为什么我错了(。

我决定对此进行测试,并看到f1返回结果,但f2返回错误。

有趣的是,如果您使用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) 

这似乎比我预期的要复杂一些。

了解为什么f1f2不等同或如何使f2enexpr一起工作会很有帮助。

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

最新更新