rlang::exec调用带包限定符的函数失败问题及R包开发场景下的实现需求问询
解决方法
要实现允许用户传入"pkg::fun"格式的函数名且无需提前加载包的需求,你可以改用rlang::call2()构造函数调用表达式,再通过rlang::eval_tidy()执行它,而不是直接把带包限定符的字符串传给rlang::exec()。
修改后的foo函数如下:
foo <- function(stuff, fun, args) { # 这里可以添加你需要的前置处理逻辑 # do stuff... # 构造函数调用并执行 call_expr <- rlang::call2(fun, !!!args) result <- rlang::eval_tidy(call_expr) # 这里添加后置处理逻辑 # do more stuff... result }
为什么原来的方法不行?
当你把"tibble::tibble"传给rlang::exec()时,exec()会将这个字符串当作单一的函数名去查找,它会尝试寻找名为"tibble::tibble"的函数(而不是解析成tibble包下的tibble函数),自然会找不到并报错。
而rlang::call2()会正确解析带::的字符串:它会把"pkg::fun"转换成pkg::fun(...)的调用表达式,再通过rlang::eval_tidy()执行时,::运算符会直接访问目标包的命名空间,不需要提前加载包到全局环境。
验证示例
- 未加载
tibble包时执行:
foo(stuff = NULL, fun = "tibble::tibble", args = list(a = 1:5))
这会正常返回一个tibble对象,无需提前运行library(tibble)。
- 传入普通函数名(如已加载包的函数或全局函数)也同样适用:
# 比如调用base包的sum函数 foo(stuff = NULL, fun = "sum", args = list(1:5, na.rm = TRUE))
另外,如果你坚持想用rlang::exec(),也可以先将带包限定符的字符串转换为函数对象,再传给exec():
foo <- function(stuff, fun, args) { # 处理fun参数,转换为函数对象 if (is.character(fun) && grepl("::", fun)) { parts <- strsplit(fun, "::")[[1]] fun_obj <- getFromNamespace(parts[2], ns = parts[1]) } else { fun_obj <- rlang::as_function(fun) } result <- rlang::exec(fun_obj, !!!args) # do more stuff... result }
这个方法通过getFromNamespace()直接从目标包的命名空间获取函数对象,同样不需要加载包,之后就可以正常用exec()调用了。
内容的提问来源于stack exchange,提问作者cmo
相关产品推荐
相关产品推荐

