Tidyeval技巧:无需用户手动引用地向嵌套函数传递含引用元素的列表
问题解决方法
核心原因
原来的写法报错是因为R在将参数传入b函数前会先对参数求值,list(var = Species)中的Species会在当前全局环境尝试求值,自然找不到仅存在于iris数据框内的Species列。
实现代码
最终的函数b写法
library(dplyr) library(rlang) library(purrr) # 用到map函数,未加载的话需要先引入 # 原函数a保持不变 a <- function(var, values){ filter(iris, {{var}} %in% values) } # 重写后的函数b b <- function(l, ...){ # 捕获l参数的原始表达式,暂不触发求值 l_raw <- enexpr(l) # 校验传入参数为list调用 if (!is_call(l_raw, "list")) { abort("参数l必须以list()形式直接传入") } # 提取list内部的所有参数,逐个转为quosure保留表达式与对应环境 l_quos <- map(call_args(l_raw), as_quosure, env = caller_env()) # 注入到a函数中执行 eval_tidy(expr(a(!!! l_quos))) }
测试调用
# 你需要的调用形式可正常运行 b(l = list(var = Species, values = "virginica")) %>% head()
运行后会得到和直接调用a函数一致的输出。
实现逻辑说明
enexpr(l)的作用是捕获用户传入l的原始写法,不会触发提前求值,从根源避免了Species找不到的错误call_args(l_raw)会把list调用内的各个参数提取出来,得到对应var和values的原始表达式- 逐个转为quosure是为了保留每个表达式的求值上下文,保证dplyr的{{ }}语法可以正确识别数据框内的列
- 最后用
!!!把quosure列表注入到a函数的调用参数中,完成嵌套调用
内容的提问来源于stack exchange,提问作者LMc
相关产品推荐
相关产品推荐

