行列引用、条件筛选等可以简单的数据管理,但其在无法有效处理多次、多重、有规律的循环和判断问题,而控制流却可以通过循环、判断、跳错等等操作轻松处理此类问题。
以下概念贯穿控制流张杰的内容,需要首先认识:
- 语句(statement):单独或组合语句,一般在{}中以;分隔 。例如:{语句1;语句2}
- 条件(cond): 最常见的是判断一个条件是否成立。如果成立则执行一条语句或者一个代码块,比如上例a是否小于b,如果小于则输出b
- 表达式(expr):一个数值或字符的求值语句,多用于数据计算过程或赋值
- 序列(seq):一个数值或者字符序列
目录
1 分支控制
1.1 if-else
1.2 ifelse
1.3 switch
2 循环控制流
2.1 for循环
2.2 while循环
2.3 repeat 循环
3 function函数(一次编写,多次调用,一劳永逸)
3.1 自定义函数编写
3.2 source()文件间调用自定义函数
分支和循环是通用编程语言中常见的两大控制流。其中,分支控制是根据条件表达式的结果,执行不同的代码段;循环控制是根据条件重复执行代码块,为了避免无限循环,可以根据条件结束循环。接下来分别从分支控制和循环控制,对R语言中的控制流做简单讲述。
正文
1 分支控制
1.1 if-else
经典的流程控制关键字是if-else,并可以把多个if-else语句连接到一起
代码语言:javascript复制#if-else分支控制流语法
if ( test_expression1) {
statement1
} else if ( test_expression2) {
statement2
} else {
statement3
}
示例
代码语言:javascript复制#嵌套用法
> a=5
> b=6
> if (a < b) {a <- -1
} else if (a == b) {a <- 0
} else {a <- 1}
> print(a)
[1] -1
1.2 ifelse
ifelse控制可以理解为一个函数。
代码语言:javascript复制#ifelse语法
ifelse(条件表达式, true, false)
示例
代码语言:javascript复制> x <- factor(sample(letters[1:5], 10, replace = TRUE))
> x
[1] a a c d a b d b d d
Levels: a b c d
> ifelse(x %in% c("a", "b", "c"), x, factor(NA))
[1] 1 1 3 NA 1 2 NA 2 NA NA
注意:返回值的class属性跟test表达式相同,其mode属性是由 yes 或 no表达式确定的。当ifelse()用于返回Date类型的对象时,返回值是numeric类型,而不是Date类型,这是因为返回值的class是由test表达式决定的。
代码语言:javascript复制> dates <- as.Date(c('2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04', '2011-01-05'))
> dates
[1] "2011-01-01" "2011-01-02" "2011-01-03" "2011-01-04" "2011-01-05"
> dates <- ifelse(dates == '2011-01-01', dates - 1, dates)
> dates
[1] 14974 14976 14977 14978 14979
#解决方案是:把返回值的class重新设置为Date类型。
> dates <- as.Date(c('2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04', '2011-01-05'))
> dates
[1] "2011-01-01" "2011-01-02" "2011-01-03" "2011-01-04" "2011-01-05"
> dates <- ifelse(dates == '2011-01-01', dates - 1, dates)
> dates
[1] 14974 14976 14977 14978 14979
> class(dates) <- 'Date'
> dates
[1] "2010-12-31" "2011-01-02" "2011-01-03" "2011-01-04" "2011-01-05"
1.3 switch
如果分支较多,可以使用switch函数实现分支的选择,switch函数的第一个参数是表达式(exp),通常是一个字符串。当表达式(exp)匹配后续的参数名(即变量名)时,返回参数的值
代码语言:javascript复制#switch语法
switch(字符,参数名1='参数值2',参数名2='参数值2',……"其他")
示例
代码语言:javascript复制#当表达式(exp)匹配后续的参数名(即变量名)时,返回参数的值
> t = "r"
> switch(t,r='re',g='gr',b='bl',"error")
[1] "re"
#如果不匹配任何参数名,switch函数不返回任何值,可以添加一个匿名的参数,
#当表达式(exp)匹配不上任意一个命名参数时,switch函数将返回匿名参数的值:
> t = "xs"
> switch(t,r='re',g='gr',b='bl',"error")
[1] "error"
2 循环控制流
repeat、while和for是常见的循环控制语句。
2.1 for循环
使用迭代器和一个向量参数,在每个循环中,迭代器变量从向量中取得一个值,直到迭代所有得向量
代码语言:javascript复制#语句
for (变量 in 序列/字符集) {语句/表达式}
示例
代码语言:javascript复制#依次执行序列/字符集中的每一个数据 print语句
> for (i in 1:5) print(letters[i])
[1] "a"
[1] "b"
[1] "c"
[1] "d"
[1] "e"
#依次执行序列/字符集中的每一个数据 表达式 print语句
> for (i in 1:5) {j = j i; print(c(i,j))}
[1] 1 1
[1] 2 3
[1] 3 6
[1] 4 10
[1] 5 15
> j = 0
> for (i in 1:5) {j = j i; print(c(i,j))}
[1] 1 1
[1] 2 3
[1] 3 6
[1] 4 10
[1] 5 15
> print(j)
[1] 15
2.2 while循环
先检测条件,如果条件为TRUE,执行code;如果条件为FALSE,结束循环
代码语言:javascript复制#语句
while (条件) {语句/表达式} #条件为TRUE,执行语句/表达式;否则终止跳出
示例
代码语言:javascript复制> i=5
> while (i >0) {j = i; i=i-1 ; print(c(j,i))}
[1] 5 4
[1] 4 3
[1] 3 2
[1] 2 1
[1] 1 0
2.3 repeat 循环 repeat 循环:先执行代码,遇到break关键字,结束循环
代码语言:javascript复制repeat {code if(条件) break}
示例
代码语言:javascript复制> i=100
> sum=0
> repeat
{
if(i==0)
break
sum=i sum
i=i-1
}
> print(sum)
[1] 5050
代码语言:javascript复制
3 function函数(一次编写,多次调用,一劳永逸)
3.1 自定义函数编写
R通过function关键字定义函数,函数主要由函数名称,参数,运行的代码块和返回值组成,函数名称是变量,参数是调用函数时需要传递的形式参数;代码块是由由大括号构成,是调用函数时需要执行的代码逻辑;R的函数不需要显式地使用return关键字明确返回值,R函数的计算的最后一个值将自动作为返回值。
代码语言:javascript复制#语法
myfunc=function(arg1,arg2,....) #参数
{
表达式/循环/print语句等
return(object) #返回输出的对象
}
示例1:简单计算
代码语言:javascript复制> avg <- function(a,b)
{
return(mean(c(a,b)))
}
> avg(4,5)
[1] 4.5
> avg <- function(a,b)
{
mean(c(a,b))
}
> avg(4,5)
[1] 4.5
示例2:矩阵计算
代码语言:javascript复制> mat1<-matrix(c(1:12),nrow = 3,ncol = 4);mat1
[,1] [,2] [,3] [,4]
[1,] 1 4 7 10
[2,] 2 5 8 11
[3,] 3 6 9 12
> mat2<-matrix(c(1:24),nrow = 4,ncol = 6);mat2
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 5 9 13 17 21
[2,] 2 6 10 14 18 22
[3,] 3 7 11 15 19 23
[4,] 4 8 12 16 20 24
> f<-function(x,y)
{
xcol<-dim(x)[2]
yrow<-dim(y)[1]
m<-dim(x)[1]
n<-dim(y)[2]
if(xcol!=yrow) #向量乘积的要求
{
print("error")
return(0)
}
else #if-else语句
{
mat<-matrix(0,nrow=dim(x)[1],ncol=dim(y)[2]) #首先要定义一个矩阵作为结果矩阵
for(i in c(1:m))
for(j in c(1:n))
mat[i,j]<-sum(x[i,]*y[,j])
return(mat)
}
}
> f(mat1,mat2) #调用自定义矩阵计算函数
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 70 158 246 334 422 510
[2,] 80 184 288 392 496 600
[3,] 90 210 330 450 570 690
> mat1%*%mat2 #验证
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 70 158 246 334 422 510
[2,] 80 184 288 392 496 600
[3,] 90 210 330 450 570 690
3.2 source()文件间调用自定义函数
在R语言里我们需要借助source()函数。
示例:自定义avgfunction函数并保存到avgfunction.R文档里
代码语言:javascript复制#avgfunction代码
avgfunction = function(x){
sum(x)/length(x)
}
source('avgfunction.R') #注意需用引号将文档名引起来,当avgfunction.R与operate.R在同一路径时,不需要加路径
将被调用的函数放置在电脑桌面(C:/Users/ysl/Desktop/),工作目录(C:/Users/ysl/Documents)
代码语言:javascript复制> source('avgfunction.R') #因被调用函数与当前工作空间不一致,提示错误
#Error in file(filename, "r", encoding = encoding) : 无法打开链结
#此外: Warning message:
#In file(filename, "r", encoding = encoding) :
# 无法打开文件'avgfunction.R': No such file or directory
#重新调整在source()添加完整的文件路径,即可成功调用
> source('C:/Users/ysl/Desktop/avgfunction.R')
> avgfunction(c(1:20))
[1] 10.5