R语言 控制流:for、while、ifelse和自定义函数function

2019-06-19 14:57:02 浏览数 (2)

行列引用、条件筛选等可以简单的数据管理,但其在无法有效处理多次、多重、有规律的循环和判断问题,而控制流却可以通过循环、判断、跳错等等操作轻松处理此类问题。

以下概念贯穿控制流张杰的内容,需要首先认识:

  • 语句(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

0 人点赞