函数,其实就是一个黑箱子,一个封闭的计算结构体,对于用户来说,只需要关注输入和输出。函数在所有的编程语言中都有实现,主要的目的是方便进行模块化编程,代码维护等。
我们可以在参数列表中加上一个省略号(…)来方便地指定任意长度的参数。
下面通过构造一个函数来举例说明。这个函数的功能是打印出第一个参数的内容,然后将剩下的所有参数传递给summary
函数。
首先构造一个函数,它有一个参数x
。这个函数的参数列表中还包含了一个省略号,因此这个省略号将成为我们调用的summary
函数的参数。
> v <- c(sqrt(1:100))
> f <- function(x, ...) { print(x); summary(...)}
> f("Here is the summary for v.", v, digits=2)
[1] "Here is the summary for v."
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.0 5.1 7.1 6.7 8.7 10.0
我们可以发现,x
之后的所有参数都被传进了summary
函数。
有时候我们想要知道这些可变参数列表的所有参数值,这时候我们需要做的是在函数内部将对象...
转换为一个列表。
举个例子,我们构造一个函数然后对所有的参数进行相加求和。
代码语言:javascript复制> addemup <- function(x, ...){
args <- list(...)
for (a in args) x <- x a
x
}
> addemup(1,1)
[1] 2
> addemup(1,2,3,4,5)
[1] 15
我们还可以通过..1
,..2
到..9
等直接引用列表...
中的内容。..1
表示第一项,..2
表示第二项,以此类推。这有点类似于shell中通过$
引用相应的参数。看来很多的编程语言都存有相同的参数传递机制。
函数的属性
R中包含了一系列的函数用于提取函数类型对象的信息。
args
函数可以用来查看函数包含了哪些参数,args
返回一个函数类型的对象,函数体为NULL
。
> args(sin)
function (x)
NULL
> args('?')
function (e1, e2)
NULL
> args(args)
function (name)
NULL
> args(lm)
function (formula, data, subset, weights, na.action, method = "qr",
model = TRUE, x = FALSE, y = FALSE, qr = TRUE, singular.ok = TRUE,
contrasts = NULL, offset, ...)
NULL
如果我们想要在R代码中对函数的参数列表进行操作,formals
函数是一个很好的工具,它会返回一个配对列表对象(对应参数名和设定的默认参数值)。
注意,formals
函数仅能运行在R写的函数上(类型为closure的对象),而不能在内嵌函数(bulti-in function)上运行。(可能是因为内嵌函数是C写的吧)
> f <- function(x, y=1, z=2) {x y z}
> f.formals <- formals(f)
> f.formals
$x
$y
[1] 1
$z
[1] 2
我们还可以利用它来更改函数的形参。例如:
代码语言:javascript复制> f.formals$y <- 3
> formals(f) <- f.formals
> args(f)
function (x, y = 3, z = 2)
NULL
R提供了一个非常方便的函数alist
用来构建参数列表。我们可以像定义函数一样很简单地指定参数列表。(注意,如果参数没有默认值,我们不必给它指定一个值,但是必须包含等号。)
> f <- function(x, y=1, z=2) { x y z}
> formals(f) <- alist(x=, y=100, z=200)
> args(f)
function (x, y = 100, z = 200)
NULL
R提供了与formals
函数类似的函数body
来返回函数的函数体:
> body(f)
{
x y z
}
我们也可以将它放在赋值语句的左边,通过赋值操作来改变函数体。
代码语言:javascript复制> f
function (x, y = 100, z = 200)
{
x y z
}
> body(f) <- expression({x * y * z})
> f
function (x, y = 100, z = 200)
{
x * y * z
}
注意,函数体的类型为expression
,所以重新赋值时,也必须为expression
类型。
改变其他环境
这里介绍<<-
操作符的使用:比如var <<- value
,它会使解释器首先在当前环境中检索寻找符号var
。如果解释器无法在当前环境中找到符号var
,那么接下来会在父环境中继续寻找。解释器将这样递归地在各个环境中寻找直到找到该符号或到达全局环境。加入解释器在到达全局环境时依然没有找到var
,那么R会在全局环境中指定var
的值为value
。
下面是一个例子:
代码语言:javascript复制> x
错误: 找不到对象'x'
> doesnt.assign.x <- function(i){ x <- i}
> doesnt.assign.x(4)
> x
错误: 找不到对象'x'
> assign.x <- function(i){ x <<- i}
> assign.x(4)
> x
[1] 4