可以使用sys.call()
来访问调用者给出的函数参数。需要注意的是,sys.call()
不会评估参数,而是给出调用的表达式。当函数被调用时,如果使用...
作为参数,则会变得特别困难: sys.call()
仅包含...
,而不包含它们的值。但是,可以将sys.call()
作为另一个函数(例如list()
)的参数列表进行评估。这将评估所有承诺并丢弃一些信息,但我无法看到如何在尝试规避R的内部匹配时摆脱这种情况。
一个想法是模拟严格匹配。如果作为函数中的第一个命令调用,则附加了一个帮助程序函数,可以实现这一点:
fun = function(x, base='', ...) {
strictify()
list(x, base, ...)
}
这将过滤掉不匹配的参数:
> fun(10, b = 20)
[[1]]
[1] 10
[[2]]
[1] ""
$b
[1] 20
并且应该在大多数其他情况下也能正常工作(带或不带...
,参数在...
右侧,带有参数默认值)。唯一无法使用的是非标准评估,例如尝试使用substitute(arg)
获取参数表达式时。
辅助函数
strictify <- function() {
parenv <- parent.frame()
rm(list=ls(parenv), envir=parenv)
scall <- sys.call(-1)
callingfun <- scall[[1]]
scall[[1]] <- quote(`list`)
args <- eval.parent(scall, 2)
if (is.null(names(args))) {
names(args) <- rep("", length(args))
}
callfun.object <- eval.parent(callingfun, 2)
callfun.header <- formals(callfun.object)
dummyfun <- call("function", callfun.header, quote(environment()))
dummyfun <- eval(dummyfun, envir=environment(callfun.object))
parnames <- names(callfun.header)
argsplit <- split(args, names(args) %in% c("", parnames))
matching.args <- c(list(), argsplit$`TRUE`)
nonmatching.arg.names <- names(argsplit$`FALSE`)
source.env <- do.call(dummyfun, matching.args)
for (varname in ls(source.env, all.names=TRUE)) {
parenv[[varname]] <- source.env[[varname]]
}
if (!"..." %in% parnames) {
if (length(nonmatching.arg.names)) {
stop(sprintf("Nonmatching arguments: %s",
paste(nonmatching.arg.names, collapse=", ")))
}
} else {
open.args <- setdiff(parnames, names(args))
taken.unnamed.args <- min(which(open.args == "...")) - 1
unmatched <- args[!names(args) %in% parnames]
unmatched[which(names(unmatched) == "")[seq_len(taken.unnamed.args)]] <- NULL
dotsenv <- do.call(function(...) environment(), unmatched)
parenv[["..."]] <- dotsenv[["..."]]
}
}
还可以编写一个函数,将通常匹配的函数转换为严格匹配的函数,例如:
strict.fun = strictificate(fun)
但那将使用相同类型的技巧。
sys.call()
,并从中检查函数是如何被调用的。 - Jouni HelskemyFunc2
,并保留旧的API(myFunc
)不变以实现向后兼容性,但将其实现更改为myFunc2
的简单包装。 - NPE