Split up `...` arguments and distribute to multiple functions
Separate Lists If you really want to pass different sets of parameters to different functions then it's probably cleaner to specify separate lists:
foo <- function(x, y, sum = list(), grep = list()) {
list(sum = do.call("sum", c(x, sum)), grep = do.call("grep", c("abc", y, grep)))
}
# test
X <- c(1:5, NA, 6:10)
Y <- "xyzabcxyz"
foo(X, Y, sum = list(na.rm = TRUE), grep = list(value = TRUE))
## $sum
## [1] 55
##
## $grep
## [1] "xyzabcxyz"
Hybrid list / ... An alternative is that we could use ... for one of these and then specify the other as a list, particularly in the case that one of them is frequently used and the other is infrequently used. The frequently used one would be passed via ... and the infrequently used via a list. e.g.
foo <- function(x, y, sum = list(), ...) {
list(sum = do.call("sum", c(x, sum)), grep = grep("abc", y, ...))
}
foo(X, Y, sum = list(na.rm = TRUE), value = TRUE)
Here are a couple of examples of the hybrid approach from R itself:
i) The mapply
function takes that approach using both ...
and a MoreArgs
list:
> args(mapply)
function (FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE, USE.NAMES = TRUE)
NULL
ii) nls
also takes this approach using both ...
and the control
list:
> args(nls)
function (formula, data = parent.frame(), start, control = nls.control(),
algorithm = c("default", "plinear", "port"), trace = FALSE,
subset, weights, na.action, model = FALSE, lower = -Inf,
upper = Inf, ...)
NULL
-
Why does
grep
error beforesum
?See that
sum
is a lot more accommodating with its arguments:X <- c(1:5, NA, 6:10) sum(X, na.rm = TRUE, value = TRUE) ## [1] 56
It doesn't failed because it doesn't care about other named arguments, so the
value = TRUE
simplifies to justTRUE
which sums to 1. Incidentally:sum(X, na.rm = TRUE) ## [1] 55
-
How to split
...
to different functions?One method (that is very prone to error) is to look for the args for the target functions. For instance:
foo <- function(x, y, ...){ argnames <- names(list(...)) sumargs <- intersect(argnames, names(as.list(args(sum)))) grepargs <- intersect(argnames, names(as.list(args(grep)))) list(sum = do.call(sum, c(list(x), list(...)[sumargs])), grep = do.call(grep, c(list("abc", y), list(...)[grepargs]))) }
This is prone to error anytime the arguments a function uses are not properly reported by
args
, such as S3 objects. As an example:names(as.list(args(plot))) ## [1] "x" "y" "..." "" names(as.list(args(plot.default))) ## [1] "x" "y" "type" "xlim" "ylim" ## [6] "log" "main" "sub" "xlab" "ylab" ## [11] "ann" "axes" "frame.plot" "panel.first" "panel.last" ## [16] "asp" "..." ""
In this case, you could substitute the appropriate S3 function. Because of this, I don't have a generalized solution for this (though I don't know that it does or does not exist).