第 4 章 流程控制

4.1 分支控制 if-else

让程序有选择的的执行,分支控制有三种:单分支、双分支、多分支

4.1.1 单分支

1)基本语法

1
2
3
if (条件表达式) {
执行代码块
}

说明:当条件表达式为 ture 时,就会执行{ }的代码。

2)案例实操

1
2
3
if (age >= 18){
println("成年")
}

4.1.2 双分支

1)基本语法

1
2
3
4
5
if (条件表达式) {
执行代码块 1
} else {
执行代码块 2
}

2)案例实操

1
2
3
4
5
if (age >= 18){
println("成年")
} else {
println("未成年")
}

4.1.3 多分支

1)基本语法

1
2
3
4
5
6
7
8
9
10
11
if (条件表达式 1) {
执行代码块 1
}
else if (条件表达式 2) {
执行代码块 2
}
……
else {
执行代码块 n

}

2)案例实操

1
2
3
4
5
6
7
8
9
10
11
if (age <= 6){
println("童年")
} else if(age < 18){
println("青少年")
} else if(age < 35){
println("青年")
} else if(age < 60){
println("中年")
} else {
println("老年")
}

(2)需求 2:Scala 中 if else 表达式其实是有返回值的,具体返回值取决于满足条件的代码体的最后一行内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
val result: String = if (age <= 6){
println("童年")
"童年"
} else if(age < 18){
println("青少年")
"青少年"
} else if(age < 35){
println("青年")
"青年"
} else if(age < 60){
println("中年")
"中年"
} else {
println("老年")
"老年"
}
println("result: " + result)

(3)需求 3:Scala 中返回值类型不一致,取它们共同的祖先类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
val result: Any = if (age <= 6){
println("童年")
"童年"
} else if(age < 18){
println("青少年")
"青少年"
} else if(age < 35){
println("青年")
age
} else if(age < 60){
println("中年")
age
} else {
println("老年")
age
}
println("result: " + result)

(4)需求 4:Java 中的三元运算符可以用 if else 实现

如果大括号{}内的逻辑代码只有一行,大括号可以省略。如果省略大括号,if 只对最近的一行逻辑代码起作用。

1
2
3
4
5
6
val res: String = if (age >= 18){
"成年"
} else {
"未成年"
}
val res2 = if (age >= 18) "成年" else "未成年"

4.2 嵌套分支

在一个分支结构中又完整的嵌套了另一个完整的分支结构,里面的分支的结构称为内层。分支外面的分支结构称为外层分支。嵌套分支不要超过 3 层。

1)基本语法

1
2
3
4
5
6
7
if(){
if(){

}else{

}
}

2)案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if (age >= 18){
println("成年")
if (age >= 35){
if (age >= 60){
println("老年")
} else {
println("中年")
}
} else {
println("青年")
}
} else {
println("未成年")
if (age <= 6){
println("童年")
} else {
println("青少年")
}

4.3 Switch 分支结构

  • 在 Scala 中没有 Switch,而是使用模式匹配来处理。

  • 模式匹配涉及到的知识点较为综合,因此我们放在后面讲解。

4.4 For 循环控制

Scala 也为 for 循环这一常见的控制结构提供了非常多的特性,这些 for 循环的特性被称为 for 推导式或 for 表达式。

4.4.1 范围数据循环(To)

定义一个循环变量,给他赋一个初值,还要定义每次循环之后这个循环变量怎么改变,还得定义一个推出循环的标记。首先比较繁琐,另外初值是从0开始的,特别是取数组的下标索引都是从0开始。这样就涉及到一个退出循环的标记是小于还是小于等于呢。当前的边界到底取还是不取,往往让我们感到麻烦。总要想一想才能得到结论,容易出错。所以Scala设计的时候就想到Java这些特点都不要,让程序员使用for没那么多困惑,用最简单的方式实现我们的功能即可。

比如带着当前的编号输出10次Hello World。那只要遍历一个1-10的范围作为编号即可。

1)基本语法

1
2
3
4
for(i <- 1 to 3){
print(i + " ")
}
println()
  • i 表示循环的变量,<- 规定 to

  • i 将会从 1-3 循环,前后闭合

2)案例实操

1
2
3
4
5
6
7
8
// java for语法: for(int i = 0; i < 10; i++){ System.out.println(i + ". hello world") }
// 1. 范围遍历
for (i <- 1 to 10){
println(i + ". hello world")
}
for (i: Int <- 1.to(10)){
println(i + ". hello world")
}

4.4.2 范围数据循环(Until)

1)基本语法

1
2
3
4
for(i <- 1 until 3) {
print(i + " ")
}
println()
  • 这种方式和前面的区别在于 i 是从 1 到 3-1

  • 即使前闭合后开的范围

2)案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    // 1. 范围遍历
for (i <- 1 to 10){
println(i + ". hello world")
}
for (i: Int <- 1.to(10)){
println(i + ". hello world")
}

// 不包含10
// for (i <- Range(1, 10)){
// println(i + ". hello world")
// }

for (i <- 1 until 10){
println(i + ". hello world")
}
1
2
3
4
5
6
7
8
9
for (i <- Array(12, 34, 53)){
println(i)
}
for (i <- List(12, 34, 53)){
println(i)
}
for (i <- Set(12, 34, 53)){
println(i)
}

4.4.3 循环守卫

1)基本语法

1
2
3
4
for(i <- 1 to 3 if i != 2) {
print(i + " ")
}
println()
  • 循环守卫,即循环保护式(也称条件判断式,守卫)。保护式为 true 则进入循环体内部,为 false 则跳过,类似于 continue。

  • 上面的代码等价

1
2
3
4
5
for (i <- 1 to 3){
if (i != 2) {
print(i + " ")
}
}

2)案例实操

需求:输出 1 到 10 中,不等于 5 的值

1
2
3
4
5
6
7
8
9
10
// 3. 循环守卫
for (i <- 1 to 10){
if (i != 5){
println(i)
}
}

for (i <- 1 to 10 if i != 5){
println(i)
}

4.4.4 循环步长

1)基本语法

1
2
3
for (i <- 1 to 10 by 2) {
println("i=" + i)
}

说明:by 表示步长

2)案例实操

需求:输出 1 到 10 以内的所有奇数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// 4. 循环步长
for (i <- 1 to 10 by 2){
println(i)
}

1
3
5
7
9
println("-------------------")

for (i <- 13 to 30 by 3){
println(i)
}
13
16
19
22
25
28
println("-------------------")

for (i <- 30 to 13 by -2){
println(i)
}
30
28
26
24
22
20
18
16
14

for (i <- 10 to 1 by -1){
println(i)
}
10
9
8
7
6
5
4
3
2
1
println("-------------------")
for (i <- 1 to 10 reverse){
println(i)
}
10
9
8
7
6
5
4
3
2
1
println("-------------------")

for (data <- 1.0 to 10.0 by 0.3){
println(data) //Double类型 精度丢失
}
1.0
1.3
1.6
1.9000000000000001
2.2
2.5
2.8
3.0999999999999996
3.3999999999999995
3.6999999999999993
3.999999999999999
4.299999999999999
4.599999999999999
4.899999999999999
5.199999999999998
5.499999999999998
5.799999999999998
6.099999999999998
6.399999999999998
6.6999999999999975
6.999999999999997
7.299999999999997
7.599999999999997
7.899999999999997
8.199999999999998
8.499999999999998
8.799999999999999
9.1
9.4
9.700000000000001
10.000000000000002

4.4.5 嵌套循环

1)基本语法

1
2
3
for(i <- 1 to 3; j <- 1 to 3) {
println(" i =" + i + " j = " + j)
}

说明:没有关键字,所以范围后一定要加;来隔断逻辑

2)基本语法

上面的代码等价

1
2
3
4
5
for (i <- 1 to 3) {
for (j <- 1 to 3) {
println("i =" + i + " j=" + j)
}
}

4.4.6 引入变量

1)基本语法

1
2
3
for(i <- 1 to 3; j = 4 - i) {
println("i=" + i + " j=" + j)
}

说明:

  • for 推导式一行中有多个表达式时,所以要加 ; 来隔断逻辑

  • for 推导式有一个不成文的约定:当 for 推导式仅包含单一表达式时使用圆括号,当包含多个表达式时,一般每行一个表达式,并用花括号代替圆括号,如下

1
2
3
4
5
6
for {
i <- 1 to 3
j = 4 - i
} {
println("i=" + i + " j=" + j)
}

2)案例实操

上面的代码等价于

1
2
3
4
for (i <- 1 to 3) {
var j = 4 - i
println("i=" + i + " j=" + j)
}

需求:输出九九乘法表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
object Test03_Practice_MulTable {
def main(args: Array[String]): Unit = {
for (i <- 1 to 9){
for (j <- 1 to i){
print(s"$j * $i = ${i * j} \t")
}
println()
}

// 简写
for (i <- 1 to 9; j <- 1 to i){
print(s"$j * $i = ${i * j} \t")
if (j == i) println()
}
}
}

需求:打印输出一个九层妖塔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 
object Test04_Practice_Pyramid {
def main(args: Array[String]): Unit = {
for (i <- 1 to 9){
val stars = 2 * i - 1
val spaces = 9 - i
println(" " * spaces + "*" * stars)
}

for (i <- 1 to 9; stars = 2 * i - 1; spaces = 9 - i){
println(" " * spaces + "*" * stars)
}

for (stars <- 1 to 17 by 2; spaces = (17 - stars) / 2){
println(" " * spaces + "*" * stars)
}
}
}

4.4.7循环返回值

1)基本语法

1
2
val res = for(i <- 1 to 10) yield i
println(res)

说明:将遍历过程中处理的结果返回到一个新 Vector 集合中,使用 yield 关键字。

注意:开发中很少使用。

2)案例实操

需求:将原数据中所有值乘以 2,并把数据返回到一个新的集合中。

1
2
3
4
5
6
7
8
9
10
object TestFor {
def main(args: Array[String]): Unit = {
var res = for(i <-1 to 10) yield {
i * 2
}
println(res)
}
}
// 输出结果:
Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

4.4.8 倒序打印

  • 1)说明:如果想倒序打印一组数据,可以用 reverse。

  • 2)案例实操:

需求:倒序打印 10 到 1

1
2
3
for(i <- 1 to 10 reverse){
println(i)
}

4.5 While 和 do..While循环控制

While 和 do..While 的使用和 Java 语言中用法相同。

大数据场景下会使用并行处理,每一个并行出来的代码块都会影响全局变量,显然最后的结果就不正确了,所以在Scala中为了大数据的应用场景设计出来的语言,其实不推荐使用while循环 甚至真正的函数式编程语言是没有变量的,不要说while循环,连for循环都没有。没有循环,都是使用递归来实现循环类似的功能。

4.5.1 While 循环控制

1)基本语法

1
2
3
4
5
循环变量初始化
while (循环条件) {
循环体(语句)
循环变量迭代
}

说明:

  • 循环条件是返回一个布尔值的表达式

  • while 循环是先判断再执行语句

  • 与 for 语句不同,while 语句没有返回值,即整个 while 语句的结果是 Unit 类型()

  • 因为 while 中没有返回值,所以当要用该语句来计算并返回结果时,就不可避免的使用变量,而变量需要声明在 while 循环的外部,那么就等同于循环的内部对外部的变量造成了影响,所以不推荐使用,而是推荐使用 for 循环

2)案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
object Test05_WhileLoop {
def main(args: Array[String]): Unit = {
// while
var a: Int = 10
while (a >= 1){
println("this is a while loop: " + a)
a -= 1
}

var b: Int = 0
do {
println("this is a do-while loop: " + b)
b -= 1
} while (b > 0)
}
}

4.5.2 do..while 循环控制

1)基本语法

1
2
3
4
5
6
循环变量初始化;

do{
循环体(语句)
循环变量迭代
} while(循环条件)

说明

  • 循环条件是返回一个布尔值的表达式

  • do..while 循环是先执行,再判断

2)案例实操

1
2
3
4
5
var b: Int = 0
do {
println("this is a do-while loop: " + b)
b -= 1
} while (b > 0)

4.6 循环中断

1)基本说明

Scala 内置控制结构特地去掉了 break和 continue,是为了更好的适应函数式编程,推荐使用函数式的风格解决break和continue的功能,而不是一个关键字。Scala中使用breakable控制结构来实现 break 和 continue 功能。

2)案例实操

需求 1:采用异常的方式退出循环

1
2
3
4
5
6
7
8
9
10
// 1. 采用抛出异常的方式,退出循环
try {
for (i <- 0 until 5){
if (i == 3)
throw new RuntimeException
println(i)
}
} catch {
case e: Exception => // 什么都不做,只是退出循环
}

需求 2:采用 Scala 自带的函数,退出循环

1
2
3
4
5
6
7
8
// 2. 使用Scala中的Breaks类的break方法,实现异常的抛出和捕捉
Breaks.breakable(
for (i <- 0 until 5){
if (i == 3)
Breaks.break()
println(i)
}
)

需求 3:对 break 进行省略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import scala.util.control.Breaks._
object TestBreak {
def main(args: Array[String]): Unit = {

breakable {
for (elem <- 1 to 10) {
println(elem)
if (elem == 5) break
}
}

println("正常结束循环")
}
}


breakable(
for (i <- 0 until 5){
if (i == 3)
break()
println(i)
}
)

需求 4:循环遍历 10 以内的所有数据,奇数打印,偶数跳过(continue)

1
2
3
4
5
6
7
8
9
10
11
object TestBreak {
def main(args: Array[String]): Unit = {
for (elem <- 1 to 10) {
if (elem % 2 == 1) {
println(elem)
} else {
println("continue")
}
}
}
}