在函数内部使用 `expr()`

Using `expr()` inside the function

Chapter 19 of Advanced R说明expr()在函数内部没有用。

但是,在下面的例子中,如果没有 expr(),我无法使函数工作。

假设我想在函数中对小标题进行分组。

data(iris)
iris %>% group_by(Species)

显而易见的方法是使用“curly curly”。

func_a <- function(data, grouping) {
  data %>% group_by({{grouping}})
}
func_a(iris, Species)

但是,如果我允许任意提供的表达式,“curly curly”将不起作用。

func_b <- function(data, ...) {
  data %>% group_by({{...}})
}
func_b(iris, Species)
# Error in (function (x)  : object 'Species' not found

最后,我发现我需要 expr() 才能让它发挥作用。

func_c <- function(data, ...) {
  grouping <- expr(...)
  data %>% group_by(!!grouping)
}
func_c(iris, Species)

Advanced R中expr()的例子是:

f1 <- function(x) expr(x)
f1(a + b + c)
#> x

我的主要问题是为什么 func_c 有效。 expr() 是否按原样使用 ... 并用 !! 评估它?为什么我们必须对 ... 采取不同的方法?

然后,我不确定为什么这不起作用。

func_d <- function(data, grouping) {
  grouping <- expr(grouping)
  data %>% group_by(!!grouping)
}
func_d(iris, Species)

我也检查了rlang manual,但是解释对我来说太简短了。

expr 阻止对代码求值。例如,尝试 运行 x 本身将失败,除非 x 是您之前声明的变量 - R 将在您评估时寻找 x 中的值它,如果没有找到这样的值,将发出错误。相比之下,expr(x) 永远不会失败(即使 x 尚未声明),因为 expr 告诉 R “只看表面价值,不要去寻找任何东西否则它可能代表”。 expr(x) 将 return 类型为“name”的东西,基本上就是一个名字。您可以将名称视为您和 R 之间的接口——它是您输入的内容,也是您传达指令的方式。 eval(expr(x))x.

相同

现在按顺序举例:

func_c <- function(data, ...) {
  grouping <- expr(...)
  data %>% group_by(!!grouping)
}
func_c(iris, Species)

这是有效的,因为 Species 将通过 ... 直接传递给 expr,并且 return 类型将是一个“名称”,它存储在变量 grouping。一个名字可以像你做的那样用 !! 求值,或者用 eval 求值。无论哪种方式,执行 !!grouping 都会导致 R 首先去寻找 grouping 变量代表的内容,找到 Species。变量被替换为它的值,最后 !!Species 将告诉 R 去寻找名为 Species 的变量,它在 group_by 函数的上下文中将为您提供名为“Species”的列。

继续你的下一个例子:

f1 <- function(x) expr(x)
f1(a + b + c)

这不起作用只是因为 expr(x) 阻止了任何计算。 R 不会去寻找 x 里面的东西,所以它永远不会找到 a + b + c,它需要 x 的面值,这就是你得到的。

最后,我们有你的最后一个例子:

func_d <- function(data, grouping) {
  grouping <- expr(grouping)
  data %>% group_by(!!grouping)
}
func_d(iris, Species)

这与您的第一个示例类似,但这里有一个额外的变量 - 名为 grouping 的参数。在您的第一个示例中,Species 直接进入函数(通过 ...),因此它未绑定到任何参数名称。在第三个示例中,Species 通过命名参数进入函数,即绑定到变量 grouping。然而,expr(grouping) 告诉 R“不要费心寻找 grouping 代表什么,我这里有我需要的一切”......所以它根本找不到 Speciesexpr(grouping) 只是给你名字 grouping,不管变量本身是什么。因此,当您尝试在 group_by 中使用 !!grouping 对其进行评估时,R 会尝试查找名为 grouping 的列名称...不用说,它没有找到它,并且你得到一个 Column grouping is not found 错误。

将其视为 expr()!! 有效地相互否定。

func_c <- function(data, ...) {
  grouping <- expr(...)
  data %>% group_by(!!grouping)
}

等同于

func_c <- function(data, ...) {
  data %>% group_by(...)            # Proper way to handle dots, by the way
}

(这不是完全等价的,因为前一个实现扩展了 expr 中的点,而后者扩展了 group_by 中的点。但是当单个列符号时,两种实现将产生相同的输出提供给 ....)

同样,

func_d <- function(data, grouping) {
  grouping <- expr(grouping)
  data %>% group_by(!!grouping)
}

等同于

func_d <- function(data, grouping) {
  data %>% group_by(grouping)         # No column `grouping` in iris
}

要使 func_d 正常工作,您需要将 expr() 替换为 enexpr()。这将捕获提供给函数的表达式,而不是表达式 grouping 本身。