忽略省略号函数中的参数缺失错误

Ignore argument missing error in ellipsis function

由于调用者的尾随逗号,以下代码生成 Error: argument is missing, with no default

new_game <- function(...) {
  list(...)
}

game <- new_game(
  c(1,2,3),
  c(1,2),
  c(3),
  c(2),
  c(1), # the comma here is the culprit
)

是否可以通过简单地忽略最后一个“参数”来避免这个错误?

我想这样设计我的函数,因为类似于 Rust 在复合文字中的方式 trailing commas in match statements or structs, or how Go has trailing commas,它可以更轻松地重新排列、添加、删除行以及提交和拉取更改。

原来在 ... 上有一个完整的 documentation,其中可以使用 ...length()...elt(x) 访问特定参数。我决定将所有内容都放在 try-catch 块中 - 如果抛出错误,可能是由于末尾缺少参数(请注意 missing(...elt(...length())) 不起作用)。

game <- function(...) {
  tryCatch(
    list(...),
    error = function(e) lapply(1:(...length()-1), function(x) ...elt(x))
  )
}

或者 rlang 专门针对此用例提供 list2

game <- function(...) {
  rlang::list2(...)
}

R 是一个奇怪的野兽,因为尽管语法明确允许尾随逗号,但它不像其他语言那样只是 discard/ignore 它们。相反,R 假装传递了一个“缺失的”参数。只要没有触及缺失的论点,那就没问题。我们甚至可以使用这种没有点的情况:

f = function (a, b, c) a + b

因为 c 从未被读取,我们不需要传递它:

f(1, 2)   # works
f(1, 2, ) # works, too

也许不是很有用。但以下内容也有效:

g = function (a, b, c) a + b + if (missing(c)) 0 else c
g(1, 2)    # 3
g(1, 2, )  # 3
g(1, 2, 3) # 6

…不幸的是,这并不能帮助我们直接捕获...

如果不使用现成的解决方案(例如 rlang::list2),捕获点(允许尾随逗号)的唯一真正方法是处理 未评估的 参数,并手动评估它们(我们可能会想尝试 missing(...elt(...length()));可惜 R 不接受)。

有多种获取未计算的点参数的方法。最简单的方法是使用 match.call(expand.dots = FALSE)$...。这给我们留下了:

new_game = function (...) {
    args = match.call(expand.dots = FALSE)$...
    nargs = length(args)
    if (identical(args[[nargs]], quote(expr = ))) nargs = nargs - 1L
    lapply(args[seq_len(nargs)], eval, envir = parent.frame())
}