函数中的参数匹配

R
Author

Rui

Published

January 17, 2023

参考 R Language Definition

R 语言中函数中的参数匹配分为 3 步:

接下来,设计一个简单的函数。这个函数可以计算二次函数的值,即:\(f(x)=ax^2+b\)。其中参数 b 默认为 0

f <- function(a, x, b=0) {
  return(a*x^2 + b)
}

参数名称精确匹配

关键字精确匹配是指在向函数传递实际参数时,实际参数的名称与函数的形式参数的名称一一对应,就算顺序不同,也可以成功传递参数。

f(a=1, x=2)
## [1] 4
f(a=1, x=2, b=3)
## [1] 7
f(x=2, a=1, b=3)
## [1] 7

参数名称部分匹配

每个剩余的实际参数都会使用部分匹配与剩余的形式参数进行比较。如果所提供的实际参数的名称恰好与形式参数匹配,则认为这两个参数匹配成功。否则,不能按照参数名称成功传递参数,比如下面代码中的的 c=3

f(1, 2, c=3)
## Error in f(1, 2, c = 3): unused argument (c = 3)

参数位置匹配

所有未匹配的形式参数都会与实际参数按照顺序一一对应。

f(a=1, x=2, 3)
## [1] 7

上面这个例子中,函数先按照名称部分匹配了 a=1x=2,接着再按照位置匹配将实际参数 3 与形式参数 b 对应起来。

f(1, 2, 3)
## [1] 7

上面这个例子中,因为实际参数没有名称,所以函数无法按照名称对参数进行匹配,于是使用位置匹配。其中实际参数 1 对应形式参数 a,实际参数 2 对应形式参数 x,实际参数 3 对应形式参数 b

Note

位置匹配严格按照顺序进行匹配。如f(2, 1, 3)代表f(a=2, x=1, b=3),而非f(x=2, a=1, b=3)

特殊的参数 {...}

例 1

参考: R for Data Science

R 中不少函数可以接受任意数量的输入,其原因在于使用了特殊参数 ...。这个参数会捕获任意数量的未匹配的参数。

sum(1, 2, 3, 4, 5)
## [1] 15
paste("a", "b", "c", sep = " ")
## [1] "a b c"
stringr::str_c("a", "b", "c")
## [1] "abc"

以上的函数还有个共同的特点:不能事先确定传入参数的个数。使用 args 对函数的参数进行查看,发现它们确实都使用了特殊参数 ...

args(mean)
## function (x, ...) 
## NULL
args(paste)
## function (..., sep = " ", collapse = NULL, recycle0 = FALSE) 
## NULL
args(stringr::str_c)
## function (..., sep = "", collapse = NULL) 
## NULL

进一步,可以将 ... 捕获的参数的值传递给另一个函数,如:

commas <- function(...) {stringr::str_c(..., collapse = ", ")}
commas(letters[1:10])
## [1] "a, b, c, d, e, f, g, h, i, j"

例 2

之前定义的函数 f 其实是一个向量化的函数,其中的参数 x 不仅可以是标量,还可以是向量。

严格地来说,R 中并没有“标量”这个概念。在 R 看来:“标量”不过是只包含一个元素的向量。

比如:

x <- seq(from=-10, to=10, by=0.01) # 生成一个从-10到10,步长为0.01的向量
y = f(1, x, 3)

然后在坐标轴上画出函数图像:

plot(x, y)

但现在,若想使用一个函数将求值函数 f 与绘图函数 plot 整合起来,应该怎么做?只需要建立一个新函数,将函数 fplot 中的参数放入其中即可:

computeAndplot <- function(a, x, b) {
  y = f(a, x, b)
  plot(x, y)
}

computeAndplot(1, x, 3)

如果我们想要进一步修改颜色、线条粗细等绘图参数,那就必须将 plot 函数中的相关参数纳入新函数 computeAndplot 中。 问题在于 plot 是一个含有众多参数的一个强大的函数,并且有时我们需要 plot 中的一部分参数,有时我们又需要 plot 中的另一部分参数。若将 plot 的所有参数手动输入到函数 computeAndplot 中,则会为定义函数和使用函数带来大量不便。

为此,使用 ... 来捕获任意数量未匹配的参数,相当于将 ... 中的形式参数打包为一个 list,在使用时传递给内部的函数 plot

computeAndplot <- function(a, x, b, ...) {
  y = f(a, x, b)
  plot(x, y, ...)
}

computeAndplot(1, x, 3, lty=1, col="blue")

这个例子中,a=1, x=x, b=3 在函数 computeAndplot 中按照上文所介绍的3个步骤进行参数匹配,而参数 lty=1, col="blue" 对应函数 computeAndplot 中的 ... 并被打包传递给内部函数 plot

computeAndplot(1, x, 3, col="blue", cex=0.5, main="f(x)=x^2+3")

小结

  • {...} 的作用:可以捕获任意数量未匹配的参数,并传递给其他函数

  • 使用场合:不能确定传入参数的个数时;用于扩写已有函数(使用一个函数去包装另一个函数)

  • 优点:参数传递简单、方便

  • 缺点:拼写错误的参数会被 ... 捕获,导致不会 raise error,难以发现输入错误