流程控制
- 选择结构(if-else)
- 选择结构(when)
- 循环结构(for)
- 循环结构(while)
经过前面的学习,我们知道,程序都是从上往下依次运行的,但是,仅仅是这样还不够,我们需要更加高级的控制语句来使得程序更加有趣。比如,判断一个整数变量,大于1则输出yes,小于1则输出no,这时我们就需要用到选择结构来帮助我们完成条件的判断和程序的分支走向。
在前面我们介绍了运算符,我们可以通过逻辑运算符和关系运算符对某些条件进行判断,并得到真或是假的结果。这一部分我们将继续使用这些运算符进行各种判断,以及实现流程控制。
选择结构(if-else)
某些时候,我们希望进行判断,只有在条件为真时,才执行某些代码,这种情况就需要使用到选择分支语句,首先我们来认识一下if
语句:
if (条件判断) 判断成功执行的代码;
if
的小括号中需要我们传入一个Boolean类型的结果,可以是一个Boolean变量,也可以是一个判断语句,反正只能接受true和false两种结果,比如下面的这个例子:
fun main() {
val a = 10
if(a == 12) //只有当a判断等于12时,才会执行下面的打印语句
println("Hello World!")
println("我是后续的语句") //if只会对紧跟着的一行代码生效,后续的内容无效
}
if
会进行判断,只有判断成功时才会执行紧跟着的语句,否则会直接跳过,注意,如果我们想要在if中执行多行代码,需要使用代码块将这些代码囊括起来(实际上代码块就是将多条语句复合到一起,使用花括号囊括)所以说,我们以后使用if时,如果分支中有多行代码需要执行,就需要添加花括号,如果只有一行代码,花括号可以直接省略,包括我们后面会讲到的else、while、for语句都是这样的,就像下面这样:
fun main() {
val a = 15
if (a > 10) { //只有判断成功时,才会执行下面的代码块中内容,否则直接跳过
println("a大于10")
println("a的值为:$a")
}
println("我是外层")
}
如果我们希望判断条件为真时执行某些代码,条件为假时执行另一些代码,我们可以在后面继续添加else语句:
fun main() {
val a = 15
if (a > 10) { //只有判断成功时,才会执行下面的代码块中内容,否则直接跳过
println("a大于10")
println("a的值为:$a")
} else { //当判断不成功时,会执行else代码块中的代码
println("a小于10")
println("a的值为:$a")
}
println("我是外层")
}
if-else
语句就像两个分支,跟据不同的判断情况从而决定下一步该做什么,这跟我们之前认识的三元运算符性质比较类似。
那如果此时我们需要判断多个分支呢?比如我们现在希望判断学生的成绩,不同分数段打印的等级不一样,比如90以上就是优秀,70以上就是良好,60以上是及格,其他的都是不及格,那么这种我们又该如何判断呢?要像这样进行连续判断,我们需要使用else-if
来完成:
fun main() {
val score = 2
if (score >= 90) //90分以上才是优秀
println("优秀")
else if (score >= 70) //当上一级if判断失败时,会继续判断这一级
println("良好")
else if (score >= 60)
println("及格")
else //当之前所有的if都判断失败时,才会进入到最后的else语句中
println("不及格")
}
当然,if
分支语句还支持嵌套使用,比如我们现在希望低于60分的同学需要补习,0-30分需要补Java,30-60分需要补C++,这时我们就需要用到嵌套:
fun main() {
val score = 2
if (score < 60) { //先判断不及格
if (score > 30) //在内层再嵌套一个if语句进行进一步的判断
println("学习C++")
else
println("学习Java")
}
}
除了if自己可以进行嵌套使用之外,其他流程控制语句同样可以嵌套使用,也可以与其他流程控制语句混合嵌套使用。这样,我们就可以灵活地使用if
来进行各种条件判断了。
除了直接执行语句之外,我们也可以将if和else用作结果判断,比如:
fun main() {
val score = 2
//这里判断socre是否大于60,是就得到Yes,否就得到No,并且可以直接赋值给变量
val res = if (score > 60) "Yes" else "No"
}
这类似于其他语言,如Java和C中的三元运算,不过Kotlin中没有那样的三元运算符,只能使用上面的表达式,对于多行代码块的情况,默认最后一行作为返回的结果:
fun main() {
val score = 2
val res = if (score > 60) {
println("不错啊期末没挂科")
"Yes" //代码块默认最后一行作为返回结果
} else {
println("不会有人Java期末还要挂科吧")
"No"
}
}
注意,如果需要这种返回结果的表达式,那么必须要存在else
分支,否则不满足条件岂不是没结果了?
选择结构(when)
前面我们介绍了if语句,我们可以通过一个if语句轻松地进行条件判断,然后根据对应的条件,来执行不同的逻辑,当然除了这种方式之外,我们也可以使用when
语句来实现,它更适用于多分支的情况:
when
定义具有多个分支的条件表达式。它类似于类似Java和C语言中的switch
语句,它简单的形式看起来像这样:
when (目标) {
匹配值1 -> 代码... //我们需要传入一个目标,比如变量,或是计算表达式等
匹配值2 -> 代码... //如果目标的值等于我们这里给定的匹配值,那么就执行case后面的代码
else -> {
代码... //如果以上条件都不满足,就进入else中(可以没有),类似于之前的if-elseif-else
}
}
比如现在我们要根据学生的等级进行分班,学生有ABC三个等级:
fun main() {
val c = 'A'
when (c) {
'A' -> println("去尖子班!准备冲刺985大学!")
'B' -> println("去平行班!准备冲刺一本!")
'C' -> println("去职高深造。")
}
}
如果将when用作表达式,则else分支必须存在,除非编译器能推断出所有可能的情况都包含分支条件,比如下面的例子:
fun main() {
val c = 'A'
val numericValue = when (c) {
'B' -> 0
'A' -> 1
else -> 2 //还有其他情况,这里必须添加else,不然其他情况岂不是没返回的东西?
}
}
以下情况就可以不需要else语句:
fun main() {
val c = true
val numericValue = when (c) {
false -> 0
true -> 1
// 由于Boolean只具备真和假条件,这里的'else' 就不再强制要求
// 这同样适用于比如枚举类等
}
}
在when
语句中,遇到以下情况,携带else
分支是必须的:
when
分支中仅有一个Boolean
类型、枚举 或 密封,以及用于判断的目标变量是可空的情况(后面会讲解)when
分支没有包括该判断目标的所有可能的值。
有时候我们可能希望某些值都属于同一个情况,可以使用逗号将其条件组合成一行:
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
我们也可以使用任意表达式(不仅仅是常量)作为分支条件,比如之前的if-else案例中我们判断学生成绩:
fun main() {
val score = 10
val grade = when(score) {
//使用in判断目标变量值是否在指定范围内
in 100..90 -> "优秀"
in 89..80 -> "良好"
in 79..70 -> "及格"
in 69..60 -> "牛逼"
else -> "不及格"
}
}
包括我们之后学习的类型判断is
表达式、函数调用等,都可以在这里作为分支条件。
循环结构(for)
通过前面的学习,我们了解了如何使用分支语句来根据不同的条件执行不同的代码,我们接着来看第二种重要的流程控制语句:循环语句。
我们在某些时候,可能需要批量执行某些代码:
fun main() {
println("大烟杆嘴里塞,我只抽第五代") //把这句话给我打印三遍
println("大烟杆嘴里塞,我只抽第五代")
println("大烟杆嘴里塞,我只抽第五代")
}
遇到这种情况,我们由于还没学习循环语句,那么就只能写N次来实现这样的多次执行。但是如果此时要求我们将一句话打印100遍、1000遍、10000遍,那么我们岂不是光CV代码就要搞一下午?
现在,要解决这种问题,我们可以使用for循环语句来多次执行:
for (遍历出来的单个目标变量 in 可遍历目标) 循环体
这里的可遍历目标有很多,比如:
- 数组
- 区间
- 任何实现了运算符重载函数iterator的类
这里我们只学习了区间,我们来看看如何使用,比如我们要打印一段话3遍:
fun main() {
for (i in 1..3) //这里直接写入1..3表示1~3这个区间
println("大烟杆嘴里塞,我只抽第五代:$i")
}
打印结果为:
可以看到,每一次遍历出来的变量i
,其实就是每次遍历的下一个目标,比如这里是1…3的区间,那么得到的依次就是1、2、3这三个结果了,唯一需要注意的是,这里的i
是局部的,只在for
循环内部可用(包括嵌套的内部)并不是整个main中都可以使用:
默认情况下,每一轮循环都会向后+1,我们也可以自由控制每一轮增加多少,也就是步长:
fun main() {
for (i in 1..10 step 2) {
println(i)
}
}
这样,打印出来的数据会按照步长进行增长:
那如果我们需要从10到1倒着进行遍历呢?我们可以将..
替换为downTo
来使用:
fun main() {
for (i in 10 downTo 1) {
println(i) //这里得到的就是10到1倒着排列的范围了
}
}
我们可以使用调试来观察每一轮的变化,调试模式跟普通的运行一样,也会执行我们的Java程序,但是我们可以添加断点,也就是说当代码运行到断点位置时,会在这里暂停,我们可以观察当代码执行到这个位置时各个变量的值:
调试模式在我们后面的学习中非常重要,影响深远,所以说各位小伙伴一定要学会。调试也很简单,我们只需要点击右上角的调试选项即可(图标像一个小虫子一样,因为调试的英文名称是Debug)
调试开始时,我们可以看到程序在断点位置暂停了:
此时我们可以观察到当前的变量i
的值,也可以直接在下方的调试窗口中查看:
随着循环的进行,i的值也会逐渐自增。
和之前的if
一样,for循环同样支持嵌套使用:
fun main() {
for (i in 0..2) //外层循环执行3次
for (j in 0..2) //内层循环也执行3次
println("外层$i,内层$j")
}
上面的代码中,外层循环会执行3轮,而整个循环体又是一个循环语句,那么也就是说,每一轮循环都会执行里面的整个循环,里面的整个循环会执行3,那么总共就会执行3 x 3次,也就是9次打印语句。
我们也可以在循环过程中提前终止或是加速循环的进行,这里我们需要认识两个新的关键字:
for (i in 0..2) {
if (i == 1) continue //比如我们希望当i等于1时跳过这一轮,不执行后面的打印
println("在这么冷的天")
println("当前i的值为:$i")
}
我们可以使用continue
关键字来跳过本轮循环,直接开启下一轮。这里的跳过是指,循环体中,无论后面有没有未执行的代码,一律不执行,比如上面的判断如果成功,那么将执行continue
进行跳过,虽然后面还有打印语句,但是不会再去执行了,而是直接结束当前循环,开启下一轮。
在某些情况下,我们可能希望提前结束循环:
fun main() {
for (i in 0..2) {
if (i == 1) break //我们希望当i等于1时提前结束
println("伞兵一号卢本伟准备就绪!")
println("当前i的值为:$i")
}
}
我们可以使用break
关键字来提前终止整个循环,和上面一样,本轮循环中无论后续还有没有未执行的代码,都不会执行了,而是直接结束整个循环,跳出到循环外部。
虽然使用break和continue关键字能够更方便的控制循环,但是注意在多重循环嵌套下,它只对离它最近的循环生效(就近原则):
fun main() {
for (i in 1..3) {
for (j in 1..3) {
if (i == j) continue //当i == j时加速循环
println("$i, $j")
}
}
}
这里的continue
加速的对象并不是外层的for,而是离它最近的内层for循环,break
也是同样的规则:
fun main() {
for (i in 1..3) {
for (j in 1..3) {
if (i == j) break //当i == j时终止循环
println("$i, $j")
}
}
}
那么,要是我们就是想要终止或者是加速外层循环呢?我们可以为循环语句打上标记:
fun main() {
outer@ for (i in 1..3) { //在循环语句前,添加 标签@ 来进行标记
inner@ for (j in 1..3) {
if (i == j) break@outer //break后紧跟要结束的循环标记,当i == j时终止外层循环
println("$i, $j")
}
}
}
关于for语句的更多用法,我们会在后续的学习中继续认识。
循环结构(while)
前面我们介绍了for循环语句,我们接着来看第二种while循环,for循环要求我们给一个可遍历的目标,而while相当于是一个简化版本,它只需要我们填写循环的维持条件即可,比如:
while(循环条件) 循环体;
相比for循环,while循环更多的用在不明确具体的结束时机的情况下,而for循环更多用于明确知道循环的情况,比如我们现在明确要进行循环10次,此时用for循环会更加合适一些,又比如我们现在只知道当i
大于10时需要结束循环,但是i
在循环多少次之后才不满足循环条件我们并不知道,此时使用while就比较合适了。
fun main() {
var i = 100 //比如现在我们想看看i不断除以2得到的结果会是什么,但是循环次数我们并不明确
while (i > 0) { //现在唯一知道的是循环条件,只要大于0那么就可以继续除
println(i)
i /= 2 //每次循环都除以2
}
}
上面的这种情况就非常适合使用while循环。
和for循环一样,while也支持使用break和continue来进行循环的控制,以及嵌套使用:
fun main() {
var i = 100
while (i > 0) {
if (i < 10) break
println(i)
i /= 2
}
}
我们也可以反转循环判断的时机,可以先执行循环内容,然后再做循环条件判断,这里要用到do-while
语句:
fun main() {
var i = 0 //比如现在我们想看看i不断除以2得到的结果会是什么,但是循环次数我们并不明确
do { //无论满不满足循环条件,先执行循环体里面的内容
println("Hello World!")
i++
} while (i < 10) //再做判断,如果判断成功,开启下一轮循环,否则结束
}