R学习 第四篇:函数和流程控制

变量用于临时存储数据,而函数用于操作数据,实现代码的重复使用。在R中,函数只是另一种数据类型的变量,可以被分配,操作,甚至把函数作为参数传递给其他函数。分支控制和循环控制,和通用编程语言的风格很相似,但是,不要因为R具有这些元素,就把R作为通用编程语言来看待,R的最小变量是向量,是一种面向数组(Array-Oriented)的语言。在编程时,尽量用array的方式思考,避免使用循环(for,while,repeat)控制,而使用apply函数家族实现计算的迭代,这是R语言的特色,把特定的函数应用于向量,列表或数组中的每个元素上。

一,R函数的风格

R通过function关键字定义函数,函数主要由函数名称,参数,运行的代码块和返回值组成,函数名称是变量,参数是调用函数时需要传递的形式参数;代码块是由由大括号构成,是调用函数时需要执行的代码逻辑;R的函数不需要显式地使用return关键字明确返回值,R函数的计算的最后一个值将自动作为返回值。

1,创建函数

例如,创建一个函数,并把函数赋值给avg变量,可以认为函数的名称是avg,其有两个参数a和b,大括号内的代码是函数体,实现的功能是求两个数的平均值。在R中创建函数时,如果函数体只有一行代码,可以省略大括号。

avg <- function(a,b)
{
    mead(c(a,b))
}

该函数没有显式调用return关键字,函数代码块种计算的最后一次自动最为返回值,在avg函数种,代码块调用系统函数mean,返回参数a和b的平均值,调用格式是函数名称(形式参数):

> avg(2,3)
[1] 2.5

调用函数时,如果不命名参数,则R按照位置匹配参数,因此,2对应形式参数a,3对应形式参数b。如果要改变传递参数的顺序,则可以传入命名参数:

> avg(b=2,a=3)
[1] 2.5

可以为函数的参数设置默认值,当调用函数时,如果没有为参数传递相应的值,那么函数将自动使用默认值作为函数代码块执行的当前值。

> avg <- function(a=1,b=1)mean(c(a,b))
> avg()
[1] 1

虽然函数中最后一行代码的值是自动返回的,但是,为了便于阅读代码,建议使用return函数,清晰地指定要返回的值,return函数除了指定函数的返回值之外,还能使函数退出,不再继续执行后面的代码。

> avg <- function(a=1,b=1) return(mean(c(a,b)))

2,把函数作为参数传递给其他函数

函数本质上是一个函数类型的变量,和其他类型的变量一样,能够作为函数的形式参数,传递给其他函数,例如:

> fn_call <- function(x,a,b)x(a,b)
> fn_call(avg,1,2)
[1] 1.5

R提供一个系统函数do.call(fun,args),用于调用其他函数,第一个参数是函数变量,第二个参数是有参数构成的列表:

> do.call(avg,args=list(1,2))
[1] 1.5

3,查看函数的定义

通过函数args用于显示函数的头部文本,用于显示函数的头部定义,例如:

> args(avg)
function (a = 1, b = 1) 
NULL

body(fun)函数用于返回函数的代码块,例如:

> body(avg)
mean(c(a, b))

除了这两个函数,还有函数formals(fun),用于查看函数的参数及其默认值,如果函数没有定义参数的默认值,那么只显示参数名,formals显示的参数名格式是$参数名。函数deparse(fun)用于查看函数调用的函数。

4,特殊的参数

R提供了一个特殊的运算符(...),该运算符允许函数具有任意多个的参数,并且不需要在函数定义中指定。

avg <- function(a,b, ...) mead(c(a,b))

二,分支控制

分支和循环是通用编程语言中必不可少的两大流程控制元素,分支控制是根据条件表达式的结果,执行不同的代码段。

1,if-else控制

经典的流程控制关键字是if-else,并可以把多个if-else语句连接到一起:

if ( test_expression1) {
statement1
} else if ( test_expression2) {
statement2
} else {
statement3
}

2,ifelse控制

ifelse控制是,在一个函数中,结合了if-else的功能,其语法是:

ifelse(test, yes, no)

当test条件为TRUE时,返回yes表达式的值,当test条件为FALSE时,返回no表达式的值。

> ifelse(c(1,2,3)>=2,'Greater','Less')
[1] "Less"    "Greater" "Greater"

对于ifelse的返回值,官方文档的描述是:

A vector of the same length and attributes (including dimensions and "class") as test and data values from the values of yes or no. The mode of the answer will be coerced from logical to accommodate first any values taken from yes and then any values taken from no.

注意:返回值的class属性跟test表达式相同,其mode属性是由 yes 或 no表达式确定的。

当ifelse()用于返回Date类型的对象时,返回值是numeric类型,而不是Date类型,这是因为返回值的class是由test表达式决定的。

dates <- as.Date(c('2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04', '2011-01-05'))
dates <- ifelse(dates == '2011-01-01', dates - 1, dates)

解决方案是:把返回值的class重新设置为Date类型。

dates <- as.Date(c('2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04', '2011-01-05'))
dates <- ifelse(dates == '2011-01-01', dates - 1, dates)
class(dates) <- 'Date'

3,switch函数

如果分支较多,可以使用switch函数实现分支的选择,switch函数的第一个参数是表达式(exp),通常是一个字符串;当表达式(exp)匹配后续的参数名(即变量名)时,返回参数的值:

> color <- function(t) switch(t,r='red',g='green',b='blue')
> color('r')
[1] "red"

如果不匹配任何参数名,switch函数不返回任何值,可以添加一个匿名的参数,当表达式(exp)匹配不上任意一个命名参数时,switch函数将返回匿名参数的值:

> color <- function(t) switch(t,r='red',g='green',b='blue', 'error')
> color('d')
[1] "error"

三,循环控制

循环控制是根据条件重复执行代码块,为了避免无限循环,可以根据条件结束循环。循环控制语句是repeat、while和for,R的向量化自带循环特性,减少了循环语句使用的场景,但是在重复执行代码时,循环控制还是非常有用。

1,repeat 循环

repeat 循环:先执行代码,遇到break关键字,结束循环,也可以在break关键字前增减if(test)语句,当指定的条件成立(为TRUE)时,执行break关键字,结束循环:

repeat {
code
if(test) break
}

2,while 循环

while 循环:先检测条件,如果条件为TRUE,执行code;如果条件为FALSE,结束循环:

while(test)
{
code
}

3,for 循环

for 循环:使用迭代器和一个向量参数,在每个循环中,迭代器变量从向量中取得一个值,直到迭代所有得向量:

for(i in c(1:5))
{
code
}

4,向量化的循环

R是向量化的编程语言,这使得R自身带有循环的特性,而不用使用循环控制语句。关于向量化的循环控制,请阅读我的博客文章

R语言学习 第九篇:plyr包

R语言学习 第十篇:操作符

 

 

参考文档:

How to prevent ifelse() from turning Date objects into numeric objects

posted @ 2017-10-31 09:26  悦光阴  阅读(2874)  评论(0编辑  收藏  举报