R语言七天入门教程五:认识并使用函数
一、什么是函数
在编程语言中,如果有一段代码需要在多次重复使用,除了复制粘贴外,还可以将其写成一个函数。函数可以很方便地实现代码复用,对于复杂的程序功能,可以将其分解为多个函数,这样可以提高代码可读性并方便后期维护。对于在函数中定义的变量,只会在当前函数中起作用,而不会与其他函数或者函数外部中同名的变量发生冲突。此外,函数可以有一个对象作为返回值,返回值就是函数执行完后返回的结果。如果需要返回多个变量,可以将结果用列表进行包装。函数的优点可以总结为:“一次定义,多处调用。一次修改,全体生效。”
二、如何定义函数
在R语言中,使用function关键字来定义函数,其一般语法格式如下
function_name <- function(arg_1, arg_2,) {
//Function body
}
各个部分的含义如下
名称 | 含义 |
函数名称(function_name) | 这是函数的实际名称。它以该名称作为对象存储在R环境中。函数名称要尽量和函数功能保持一致,以提高可读性。 |
参数(args) | 参数是一个占位符。调用函数时,将一个值传递给参数。参数是可选的;也就是说,一个函数可能不包含任何参数。另外,参数可以具有默认值。参数名称要尽量和参数的具体含义保持一致,以提高代码可读性 |
函数体(Function body) | 函数体包含定义函数函数的语句集合。 |
返回值(return value) | 函数的返回值是函数主体中最后一个表达式,或者通过 |
例如,如果我们要计算一个数的阶乘,可以定义一个函数factorial(),代码例子如下:
factorial<-function(num){
if(num<0)
print('Input number must be a nonnegative integer') # 输入数值应该是一个非负数
else if(num == 0) # 0的阶乘为1
return (1)
else{
result = 1
for (i in 1:num) # for循环计算阶乘,1:num表示起始值为1、结尾值为num、且间隔为1的向量
result = i * result
return (result) # return语句包含()
}
}
# 定义好函数之后就可以进行调用了
factorial(-1)
factorial(0)
factorial(10)
运行结果如下:
三、函数的调用
1、形参和实参
函数定义时,所定义的参数叫形参,调用函数时,所传入的参数叫实参。(形参在定义时,程序并未为其分配内存空间和具体的值,可以理解为记号(占位符),因此称为形式参数,简称形参。而实参是调用函数时,传递给函数参数,程序已经为其分配内存空间并且值已经确定,因此称为实际参数,简称实参。)
在函数调用传递参数时,形参与实参是按位置结合的,形参表和实参表中对应的变量名可以不必相同,但它们的数据类型、参数个数及位置必须一一对应。换言之,如果形参中定义了三个变量,那一般而言我也要传入三个类型相同的实参与之对应。
程序例子如下:
# 无形参,在控制台打印'hello'
hello<-function(){
print('hello')
}
# 一个形参,返回一个数的绝对值
abs<-function(x){
# ifelse也是一个函数,其语法为if(condition,statement1,statement2)
# 含义是如果condition为TRUE,就返回statement1,否则返回statement2
return (ifelse(x>0,x,-x))
}
# 两个形参,返回两数之和
add1<-function(x,y){
return (x+y) # 更推荐使用return语句来返回值,更直观,代码可读性更高
}
# 两个形参,最后一句表达式为函数返回值
add2<-function(x,y){
x + y
}
# 调用函数
hello()
abs(-100)
add1(2,5)
add2(2,5)
结果如下,可以看到函数add1
与add2
是等价的,只不过前者用return来显式指定返回值,后者用最后一句表达式作为返回值。
2、设置缺省值
在声明函数原型时,可为一个或多个参数指定缺省参数值,以后调用此函数,若省略其中某一参数,程序自动地以缺省值作为相应参数的值。换言之,缺省值,就是在实参缺失时形参的默认值。在设置缺省值时,需要从右往左为形参设置缺省值,而在实参与形参匹配时,是从左往右依次匹配。换言之,如果当前位置的形参设置了缺省值,那么它右边的所有形参都要设置缺省值。对于未设置缺省值的形参,其实参不可省略。为了函数调用时,参数的赋值情况更明确清晰,可以在调用时给形参赋值带上参数名。
程序例子如下:
# 计算三数之和,并为z设置缺省值1
add<-function(x,y,z=1){
return (x+y+z)
}
add(2,4,5) # 赋值情况为:x=2,y=4,z=5
add(x=2,y=4,z=5) # 等价于add(2,4,5),更推荐用这种方式来调用函数
add(2,3) # 赋值情况为:x=2,y=3,z=1;z的值为缺省值1
add(2) # 调用会出现错误,此时x赋值为2,z取缺省值1,但没有给y赋值,所以会出错
结果如下:
3、懒惰计算与延迟绑定
函数的参数是惰性计算的,也就是说,只有当函数真正用到这些参数时,才会把实参的值传递给它。如果在函数中没有用到这个形参,那么实参传不传值都不会影响函数运行。为了提升代码可读性,非常不建议使用懒惰计算来传递参数。
程序例子如下:
func1<-function(x){
print(10) # 由于函数中未用到参数x,所以不管给不给x传值,都不影响函数运行
}
func1() # 可以正常运行
func1(x=100) # 也可以正常运行
func1(10,20) # 报错,因为只有一个形参,却传入了两个实参
func2<-function(x,y){
print(x) #如果只传入参数x,那么这句还可以正常运行
print(y) # 到这里才会报错
}
func2(20)
运行结果如下:
四、常用函数
在R语言中,提供了很多内置函数,这些内置函数极大地方便了我们进行数值运算和数据处理。常用函数整理如下,更多函数可参考R语言常用函数总结大全
1、程序例子
# 序列相关函数:seq()函数是冒号运算符的推广,rep()函数用来产生重复数值。
# seq(from = 1, to = 1, by = ((to - from)/(length.out - 1)),length.out = NULL, along.with = NULL, ...)
# rep(x, times = 1, length.out = NA, each = 1)
# seq函数用于生成起始值为from,末尾值为to,间隔为by的序列 (也可以通过length.out指定序列长度)
seq(5) # 等同于1:5,结果等价于c(1,2,3,4,5)
seq(3,6) # 等同于3:6,结果等价于c(3,4,5,6)
seq(6,10,2) #产生始值为6,末尾值为10,间隔为2的序列, 结果等价于c(6,8,10)
seq(0,2,length.out=4) # 产生从0到2的等间隔序列, 序列长度指定为4
rep(0, 3) # 把0重复3次,生成一个长度为3的向量,结果等价于c(0,0,0)
rep(c(1,3), 2) # 把第一个向量重复两次, 结果相当于c(1,3,1,3)
# 利用R的一般向量化规则, 即元素对应规则
# 把第一自变量的第一个元素1按照第二自变量中第一个元素2的次数重复
# 把第一自变量中第二个元素3按照第二自变量中第二个元素4的次数重复
rep(c(1,3), c(2,4)) # 结果相当于c(1,1,3,3,3,3)
# 如果希望重复完一个元素后再重复另一元素,用each参数
rep(c(1,3), each=2) # 想把1重复2次,再把3重复2次,结果相当于c(1,1,3,3)
# 统计相关函数:sum(求和), mean(求平均值), var(求样本方差), sd(求样本标准差),
# min(求最小值), max(求最大值), range(求最小值和最大值)。prod求所有元素的乘积。
# cumsum和cumprod计算累加和累乘积,即从第一个元素开始到第n个元素为止,累加和累乘的结果
c1 = c(3,5,8,1,6)
sum(c1) # 对向量c1求和
range(c1) # 统计向量c1的最小值和最大值
prod(c1) # 对向量c1,求所有元素的乘积
sort(c1) # 对向量c1排序
cumsum(1:5) # 结果为 1 3 6 10 15
cumprod(1:5) # 结果为 1 2 6 24 120
运行结果如下:
2、查看函数文档
如果在编程时遇到自己不熟悉的函数,可以使用?+函数名
来查看函数文档。例如,查询seq
函数,可以输入?seq
文档中对函数功能、用法、参数、例子都有非常详细的描述,极大地方便了编写代码。