如何用rlang的arg_match批量检查R函数的输入参数?
批量验证R函数参数取值合法性的方法
我需要快速检查函数中多个只能取特定值的参数,逐个调用rlang::arg_match可以实现需求:
fn <- function(x = c("foo", "bar"), y = c("bar", "baz")) { rlang::arg_match(x) rlang::arg_match(y) } fn(x = "bar", y = "bar") # [1] "bar" fn(x = "baz", y = "baz") # Error in `fn()`: # ! `x` must be one of "foo" or "bar", not "baz". # ℹ Did you mean "bar"? # Run `rlang::last_trace()` to see where the error occurred. fn(x = "bar", y = "foo") # Error in `fn()`: # ! `y` must be one of "bar" or "baz", not "foo". # Run `rlang::last_trace()` to see where the error occurred.
但尝试用lapply批量处理时,变量会被当作调用而非符号传入,出现报错:
# 错误尝试1:传入参数值而非符号 fn <- function(x = c("foo", "bar"), y = c("bar", "baz")) { lapply(c(x, y), function(i) rlang::arg_match(enquo(i))) } fn(x = "bar", y = "bar") # Error in `rlang::arg_match()`: # ! `arg` must be a symbol, not a call. # Run `rlang::last_trace()` to see where the error occurred. # 错误尝试2:用get获取参数值 fn <- function(x = c("foo", "bar"), y = c("bar", "baz")) { lapply(c("x", "y"), function(i) rlang::arg_match(get(i))) } fn(x = "bar", y = "bar") # Error in `rlang::arg_match()`: # ! `arg` must be a symbol, not a call. # Run `rlang::last_trace()` to see where the error occurred. # 错误尝试3:用eval获取参数值 fn <- function(x = c("foo", "bar"), y = c("bar", "baz")) { lapply(c("x", "y"), function(i) rlang::arg_match(eval(i))) } fn(x = "bar", y = "bar") # Error in `rlang::arg_match()`: # ! `arg` must be a symbol, not a call. # Run `rlang::last_trace()` to see where the error occurred. # 错误尝试4:用{{}}拼接符号 fn <- function(x = c("foo", "bar"), y = c("bar", "baz")) { lapply(c("x", "y"), function(i) rlang::arg_match({{ i }})) } fn(x = "bar", y = "bar") # Error in switch(type, call = "prefix", control = , delim = , subset = "special", : # EXPR must be a length 1 vector # 错误尝试5:传入参数值用{{}} fn <- function(x = c("foo", "bar"), y = c("bar", "baz")) { lapply(c(x, y), function(i) rlang::arg_match({{ i }})) } fn(x = "bar", y = "bar") # Error in switch(type, call = "prefix", control = , delim = , subset = "special", : # EXPR must be a length 1 vector
正确的批量处理方法
rlang::arg_match需要接收符号而非参数值或调用表达式,以下是几种可行的批量验证方案:
方法1:用rlang::sym()转换字符串为符号
通过字符串参数名转换为符号,配合非标准求值传递给arg_match,保留原生报错提示:
library(rlang) fn <- function(x = c("foo", "bar"), y = c("bar", "baz")) { # 定义参数名与对应可选值的映射 arg_options <- list( x = c("foo", "bar"), y = c("bar", "baz") ) # 遍历参数名批量验证 lapply(names(arg_options), function(arg_name) { arg_sym <- sym(arg_name) arg_match(!!arg_sym, values = arg_options[[arg_name]]) }) # 返回处理后的参数(可选) list(x = x, y = y) } fn(x = "bar", y = "bar") # $x # [1] "bar" # $y # [1] "bar" fn(x = "baz", y = "baz") # Error in `arg_match()`: # ! `x` must be one of "foo" or "bar", not "baz". # ℹ Did you mean "bar"? # Run `rlang::last_trace()` to see where the error occurred.
方法2:使用rlang::arg_match0()更高效
arg_match0()无需传入符号,直接接收参数值和可选值,更适合批量场景,还能手动指定报错中的参数名:
library(rlang) fn <- function(x = c("foo", "bar"), y = c("bar", "baz")) { arg_list <- list(x = x, y = y) arg_options <- list( x = c("foo", "bar"), y = c("bar", "baz") ) # 批量匹配验证 mapply(function(val, opts, arg_name) { arg_match0(val, opts, arg = arg_name) }, arg_list, arg_options, names(arg_list)) list(x = x, y = y) } fn(x = "bar", y = "foo") # Error in `arg_match0()`: # ! `y` must be one of "bar" or "baz", not "foo". # Run `rlang::last_trace()` to see where the error occurred.
方法3:base R原生方案match.arg()
如果不想依赖rlang包,可使用base R的match.arg()实现批量验证:
fn <- function(x = c("foo", "bar"), y = c("bar", "baz")) { arg_list <- list(x = x, y = y) arg_options <- list( x = c("foo", "bar"), y = c("bar", "baz") ) # 批量匹配验证 mapply(function(val, opts) { match.arg(val, opts) }, arg_list, arg_options) list(x = x, y = y) } fn(x = "baz", y = "baz") # Error in match.arg(val, opts) : # 'arg' should be one of "foo", "bar"
内容的提问来源于stack exchange,提问作者dragon951
相关产品推荐
相关产品推荐

