拆分`…`参数并分发给多个函数
使用下面的函数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
-
为什么
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
-
如何分割
...
到不同的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"