拆分`...`参数并分配给多个函数

时间:2014-08-19 05:27:01

标签: r

使用以下函数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()的正式论据是NULL,因为它是.Primitive,因此我无法使用

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

我也不想假设来自

的剩余参数
names(formals(grep)) %in% names(list(...))

会自动传递给sum()

如何安全有效地将...参数分发给多个函数,以便不进行不必要的评估?

从长远来看,我希望能够将此应用于包含...个参数列表的函数,类似于download.file()scan()的参数。< / p>

3 个答案:

答案 0 :(得分:25)

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

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"

混合列表/ ... 另一种选择是我们可以使用...作为其中之一,然后将另一个指定为列表,特别是在经常使用其中一个的情况下而另一种则很少使用。经常使用的一个将通过...传递,并且通过列表不经常使用。 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)

以下是R本身的混合方法的几个例子:

i)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 :(得分:13)

  1. 为什么grep之前sum错误?

    看到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. 如何将...拆分为不同的功能?

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

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

    如果args未正确报告函数使用的参数(例如S3对象),则很容易出错。举个例子:

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

    在这种情况下,您可以替换相应的S3功能。因此,我没有这方面的通用解决方案(虽然我不知道它确实存在或不存在)。

答案 2 :(得分:0)

如果其他函数包含您传递给...的所有命名参数,或者它本身有...参数,则只能将...参数传递给另一个函数。因此对于sum,这没有问题(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。一个简单的解决方案是定义您自己的函数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"