拆分`…`参数并分发给多个函数

使用下面的函数foo()作为一个简单的例子,如果可能的话,我想分配给两个不同函数的值。

 foo <- function(x, y, ...) { list(sum = sum(x, ...), grep = grep("abc", y, ...)) } 

在下面的例子中,我想将na.rm传递给sum() ,并将value传递给grep() 。 但是我在grep()得到一个未使用参数的错误。

 X <- c(1:5, NA, 6:10) Y <- "xyzabcxyz" foo(X, Y, na.rm = TRUE, value = TRUE) # Error in grep("abc", y, ...) : unused argument (na.rm = TRUE) 

这似乎是参数首先发送到grep() 。 那是对的吗? 我会认为R会首先看到并评估sum() ,并返回一个错误。

此外,在试图分裂...的论点时,我遇到了麻烦。 sum()的forms参数是NULL因为它是一个.Primitive ,因此我不能使用

 names(formals(sum)) %in% names(list(...)) 

我也不想假设这些剩余的论据

 names(formals(grep)) %in% names(list(...)) 

将被自动传递给sum()

我怎样才能安全有效地将...参数分配给多个函数,以便不进行不必要的评估?

从长远来看,我希望能够应用这个函数与长长的参数列表,类似于download.file()scan()

单独的列表如果您真的想将不同的参数集传递给不同的函数,那么指定单独的列表可能更清晰:

 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" 

混合列表/ …另一种方法是我们可以使用…作为其中之一,然后将另一个指定为列表,特别是在其中一个被频繁使用而另一个不常使用的情况下。 经常使用的将通过…传递,而不经常使用通过列表。 例如

 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) 

这里有几个R本身的混合方法的例子:

我) mapply函数采用这种方法使用...和一个MoreArgs列表:

 > args(mapply) function (FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE, USE.NAMES = TRUE) NULL 

ii) nls也采用这种方法使用...control列表:

 > 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 
  1. 为什么sum之前的grep错误?

    看到这个sum更符合它的论点:

     X <- c(1:5, NA, 6:10) sum(X, na.rm = TRUE, value = TRUE) ## [1] 56 

    它不会失败,因为它不关心其他命名参数,所以value = TRUE简化为TRUE ,其总和为1.顺便说一下:

     sum(X, na.rm = TRUE) ## [1] 55 
  2. 如何分割...到不同的function?

    一种方法(非常容易出错)是查找目标函数的参数。 例如:

     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]))) } 

    如果函数使用的参数没有被诸如S3对象之类的args正确报告,那么这很容易出错。 举个例子:

     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" "..." "" 

    在这种情况下,您可以replace相应的S3function。 因此,我没有一个广义的解决scheme(虽然我不知道它是否存在)。

你只能将...parameter passing给另一个函数,如果这个函数包含所有传递给你的命名参数,或者它有一个参数本身。 所以总之,这是没有问题的( args(sum)返回function (..., na.rm = FALSE) )。 另一方面, grep既没有na.rm也没有...作为一个参数。

 args(grep) # function (pattern, x, ignore.case = FALSE, perl = FALSE, value = FALSE, # fixed = FALSE, useBytes = FALSE, invert = FALSE) 

这不包括...也不包括命名参数na.rm 一个简单的解决scheme是定义你自己的函数mygrep ,如下所示:

 mygrep <- function (pattern, x, ignore.case = FALSE, perl = FALSE, value = FALSE, fixed = FALSE, useBytes = FALSE, invert = FALSE, ...) grep(pattern, x, ignore.case, perl, value, fixed, useBytes, invert) 

那么它似乎工作:

 foo <- function(x, y, ...){ list(sum = sum(x, ...), grep = mygrep("abc", y, ...)) } X <- c(1:5, NA, 6:10) Y <- "xyzabcxyz" foo(X, Y, na.rm = TRUE, value = TRUE) # $sum # [1] 56 # # $grep # [1] "xyzabcxyz"