Tidyeval:如何在没有用户引用的情况下将带有引用元素的列表传递给嵌套函数?

Tidyeval: How to pass a list with quoted elements to a nested function without user quoting?

假设我有两个函数,我想将 a 嵌套在 b 中:

library(dplyr)
library(rlang)

a <- function(var, values){
  filter(iris, {{var}} %in% values)
}

a(Species, "virginica") %>% head()

  Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
1          6.3         3.3          6.0         2.5 virginica
2          5.8         2.7          5.1         1.9 virginica
3          7.1         3.0          5.9         2.1 virginica
4          6.3         2.9          5.6         1.8 virginica
5          6.5         3.0          5.8         2.2 virginica
6          7.6         3.0          6.6         2.1 virginica

b 调用 a,但使用点-点-点 ... 做一些其他事情。它有一个传递给 a:

的列表参数
b <- function(l, ...){
  eval_tidy(expr(a(!!! l)))
}

b(l = list(var = quo(Species), values = "virginica")) %>% head() # works if user passes quosure

当某些元素需要引用时,我可以将参数列表传递给 b 以在 a 中进行评估吗?所以对 b 的调用将如下所示:

b(l = list(var = Species, values = "virginica")) # does not work

我已经尝试了以下方法,但似乎我需要再拆分拼接一次或进行其他操作:

b <- function(l, ...){
  l <- enquos(l)
  qq_show(a(!!! l))
}

b(l = list(var = Species, values = "virginica"))

# a(^list(var = Species, values = "virginica"))

其他尝试导致 Species 过早评估,因此出现错误:object 'Species' not found.

创建一个辅助函数来收集 a() 的参数。这个助手可以将 varenquo() 化解,因此用户不必使用 quo().

a <- function(var, values) {
  filter(iris, {{ var }} %in% values)
}

a_opts <- function(var, values) {
  list(var = enquo(var), values = values)
}

然后 b() 期望用户传递可能由 a_opts() 创建的参数列表。但是我们如何将这些参数传递给 a()?我们不能直接使用!!!,因为拼接仅在...中启用(参见dynamic dots)而a()不采用...

一种解决方案是使用 do.call()。 rlang 惯用的另一个解决方案是使用 inject():

显式启用 !!!
b <- function(l) {
  inject(a(!!!l))
}

用法示例:

a_opts(Species, "virginica") %>%
  b() %>%
  head()