有时为现有函数编写包装函数很有用。在这个简短的示例中,我们演示了如何获取传递给函数的参数列表并使用它来调用另一个函数,同时处理带有或不带默认值的可选参数。
R 函数的作者通常为函数参数指定默认值。函数的调用可能会覆盖某些参数的默认值并接受其他参数的默认值。在具有默认参数值的函数内部,参数总是有一个值,即使它是 NA 或 NULL——它们永远不会“丢失”。
此类函数中的健全性检查通常会在使用参数之前测试参数,如下例所示:
f1 <- function(a='A',b=NULL) {
print( ifelse( is.null(a), 'a not specified', paste('a =',a) ) )
print( ifelse( is.null(b), 'b not specified', paste('b =',b) ) )
}
一些快速测试显示了它是如何工作的:
f1 <- function(a='A',b=NULL) {
print( ifelse( is.null(a), 'a not specified', paste('a =',a) ) )
print( ifelse( is.null(b), 'b not specified', paste('b =',b) ) )
}
到目前为止,一切都很好。
但是当函数作者没有为可选参数提供默认值而是使用 R 的 missing() 函数来确定是否指定了可选参数时会发生什么?
f1 <- function(a='A',b=NULL) {
print( ifelse( is.null(a), 'a not specified', paste('a =',a) ) )
print( ifelse( is.null(b), 'b not specified', paste('b =',b) ) )
}
这里的情况看起来有所不同:
f1 <- function(a='A',b=NULL) {
print( ifelse( is.null(a), 'a not specified', paste('a =',a) ) )
print( ifelse( is.null(b), 'b not specified', paste('b =',b) ) )
}
输出同样正确,但可能没那么有用。
通常,推荐的做法是创建每个可选参数都具有默认值 NULL 的函数。然而,有时我们想为另一个不遵守这种做法的函数编写一个包装函数。在这种情况下,我们需要将带有一些潜在 NULL 的传入参数集转换为缺少一些参数的新集。
为此,我们将使用 match.call() 函数在函数被调用时获取参数列表(丢弃 match.call() 中的第一项,即函数名称)。然后我们可以修改这个用于包含包装函数认为重要的默认值的实际参数列表。
以下示例显示了如何处理此问题:
f1 <- function(a='A',b=NULL) {
print( ifelse( is.null(a), 'a not specified', paste('a =',a) ) )
print( ifelse( is.null(b), 'b not specified', paste('b =',b) ) )
}
作为包装函数的作者,我们为某些参数指定非 NULL 默认值。我们将强制将这些参数包含在 argList 中,无论它们是否在命令行中指定。任何值为 NULL 的参数都不会出现在 argList 中,因此当我们执行调用(f2,argList)时将“丢失”:
f1 <- function(a='A',b=NULL) {
print( ifelse( is.null(a), 'a not specified', paste('a =',a) ) )
print( ifelse( is.null(b), 'b not specified', paste('b =',b) ) )
}
请注意我们如何传递 a='A' 的默认值,但仍然允许用户将其设置为 NULL,以防 f2() 中的 missing() 测试很重要。
通常,函数作者应避免使用 missing() 构造,并且应始终为可选参数指定默认值 NULL。这允许进行更系统的健全性检查并将参数传递给其他函数。但是有大量代码可以测试 missing() 参数,我们需要知道如何使用它。