我正在编写一个接受可变数量参数的函数。此外,我希望用户能够将这些参数中的一些遗漏。
只考虑将...
转换为参数列表的任务。这是我的第一次尝试:
f <- function(...) list(...)
这失败了:
f(1,,2)
## Error in f(1, , 2) : argument is missing, with no default
我希望结果为list(1, NULL, 2)
。 (我并不担心将f(1,,2)
与f(1,NULL,2)
区分开来。)
这是我的第二次尝试:
g <- function(...)
{
args <- match.call()
miss <- vapply(args, identical, NA, quote(expr=))
args[miss] <- list(NULL)
args[[1L]] <- quote(list)
eval.parent(args)
}
这有效(某种程度上):
identical(g(1,,2), list(1, NULL, 2))
## [1] TRUE
但转发点时失败:
gg <- function(...) g(...)
identical(gg(1,,2), list(1,NULL,2))
## [1] FALSE
有没有办法可以获得与g
相同的结果,但是允许转发点而不使用match.call()
?
尝试以下方法很有吸引力:
h <- function(...)
{
args <- substitute(list(...))
miss <- vapply(args, identical, NA, quote(expr=))
args[miss] <- list(NULL)
eval.parent(args)
}
但是,正如@lionel指出的那样,如果您尝试从另一个函数转发...
,则会出现问题:
hh <- function(...) h(letters, ...)
local({ a <- "foo"; h(a) })
## Error in eval(expr, p) : object 'a' not found
您似乎需要expand.dots
的{{1}}功能,或者您需要使用quosure(来自 rlang 包)。
答案 0 :(得分:9)
使用rlang包,您可以选择在捕获点时忽略空参数:
my_dots <- function(...) {
rlang::dots_list(..., .ignore_empty = "all")
}
my_dots(1, , 2, )
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] 2
如果你真的需要将空参数翻译为NULL
,除了引用点并在转换缺失的参数后评估它们之外别无选择。现在,如果您使用eval(substitute(alist(...))
执行此操作,如果您尝试通过多个函数调用转发点,则会遇到麻烦。 Base R无法在原始环境中评估引用的参数。
幸运的是,通过捕获数据中的点来轻松实现:
library("purrr") # For convenient map_if()
my_dots <- function(...) {
quos <- rlang::quos(..., .ignore_empty = "none")
quos <- map_if(quos, rlang::quo_is_missing, function(x) NULL)
map(quos, rlang::eval_tidy)
}
my_dots(1, , 2, )
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> NULL
#>
#> [[3]]
#> [1] 2
#>
#> [[4]]
#> NULL
答案 1 :(得分:0)
h <- function(...)
{
args <- substitute(list(...))
miss <- vapply(args, identical, NA, quote(expr=))
args[miss] <- list(NULL)
eval.parent(args)
}
但是,从其他功能转发...
可能会出现问题。