解包...缺少参数

Pat*_*rry 3 r

我正在编写一个接受可变数量参数的函数.此外,我希望用户能够将这些参数中的一些遗漏.

考虑将...变成参数列表的任务.这是我的第一次尝试:

f <- function(...) list(...)
Run Code Online (Sandbox Code Playgroud)

这失败了:

f(1,,2)
## Error in f(1, , 2) : argument is missing, with no default
Run Code Online (Sandbox Code Playgroud)

我希望结果如此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)
}
Run Code Online (Sandbox Code Playgroud)

这工作(有点):

identical(g(1,,2), list(1, NULL, 2))
## [1] TRUE
Run Code Online (Sandbox Code Playgroud)

但是,转发点时失败:

gg <- function(...) g(...)
identical(gg(1,,2), list(1,NULL,2))
## [1] FALSE
Run Code Online (Sandbox Code Playgroud)

有没有办法实现相同的结果g,但在某种方式允许转发点而不使用match.call()

编辑:转发点的问题.

尝试以下方法很有诱惑力:

h <- function(...)
{
    args <- substitute(list(...))
    miss <- vapply(args, identical, NA, quote(expr=))
    args[miss] <- list(NULL)
    eval.parent(args)
}
Run Code Online (Sandbox Code Playgroud)

但是,正如@lionel指出的那样,如果您尝试...从另一个函数转发,则会出现问题:

hh <- function(...) h(letters, ...)
local({ a <- "foo"; h(a) })
## Error in eval(expr, p) : object 'a' not found
Run Code Online (Sandbox Code Playgroud)

看起来你要么需要expand.dots功能match.call,要么需要使用quosure(来自rlang包).

Lio*_*nry 9

使用rlang包,您可以选择在捕获点时忽略空参数:

my_dots <- function(...) {
  rlang::dots_list(..., .ignore_empty = "all")
}

my_dots(1, , 2, )
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] 2
Run Code Online (Sandbox Code Playgroud)

如果你真的需要翻译空参数,NULL除了引用点并在转换缺失参数后评估它们之外别无选择.现在,如果您这样做,eval(substitute(alist(...))如果您尝试通过多个函数调用转发点,则会遇到麻烦.Base R无法在原始环境中评估引用的参数.

幸运的是,通过捕获quosures中的点可以很容易地做到:

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
Run Code Online (Sandbox Code Playgroud)