解包...缺少参数

时间:2017-11-15 19:45:56

标签: r

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

只考虑将...转换为参数列表的任务。这是我的第一次尝试:

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 包)。

2 个答案:

答案 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)

@lionel给了我我需要的大部分信息,所以我将他的答案标记为正确。以下似乎有效:

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

但是,从其他功能转发...可能会出现问题。