零、本节学习目标
- 掌握条件表达式
- 掌握各种循环
- 理解流间变量绑定
- 掌握yield语句的使用
- 掌握异常处理语句
- 了解match语句的使用
- 理解变量作用域
Scala提供的控制结构并不算多,因为在函数式编程中,可以自己开发出各种功能的控制结构,所以Scala提供的原生控制结构仅仅够用为止。
一、条件表达式
(一)语法格式
1. if (条件) 值1 else 值2
(二)执行情况
- 条件为真,结果是值1;条件为假,结果是值2。如果if和else的返回结果同为某种类型,那么条件表达式结果也是那种类型,否则就是
Any
类型 - Scala的条件表达式有点类似于Java的条件运算符
(三)案例演示
任务1、根据输入值的不同进行判断
当然也可以在一个表达式中进行多次判断
可以将上述条件表达式改造成嵌套的选择结构,可读性倒是提高了,但是简洁性降低了
任务2、编写Scala程序,判断奇偶性
- 打开Scala项目
ScalaDemo
,创建net.cl.day02
包,在包里创建Example01
对象package net.cl.day02 import scala.io.StdIn /** * 功能:判断奇偶性 * 作者:cl * 日期:2023年02月22日 */ object Example01 { def main(args: Array[String]): Unit = { print("n = ") val n = StdIn.readLine.toInt if (n % 2 == 0) println(n.toString + "是偶数~") else println(n.toString + "是奇数~") } }
运行程序,查看运行结果:
-
利用
if
结构具有返回值的特性,改写程序成为函数式风格(创建Example02)
package net.cl.day02
object Example02 {
import scala.io.StdIn
def main(args: Array[String]): Unit = {
print("n = ")
val n = StdIn.readLine.toInt
val result = if (n % 2 == 0)
n.toString + "是偶数~"
else
n.toString + "是奇数~"
println(result)
}
}
运行程序,查看运行结果:
二、块表达式
(一)语法格式
{语句组}
- 块表达式为包含在符号“{}”中的语句块
(二)执行情况
- 需要注意的是,Scala中的返回值是最后一条语句的执行结果,而不需要像Java一样单独写
return
关键字。如果表达式中没有执行结果,就返回一个Unit
对象,类似Java中的void
。
(三)案例演示
for (变量 <- 集合或数组 (条件)) {
语句组
}
2、执行情况
- 表示将集合或数组中的每一个值循环赋给一个变量
3、案例演示
-
两种方式实现,一种使用Range类,一种使用to运算符
-
Range(a, b): 从a到b,不包含b,跟Python里的range函数一样,含头不含尾
-
1 to 10表示将1到10的所有值组成一个集合,且包括10。若不想包括10,则可使用关键字
until
-
打印字符直角三角形
-
利用map函数产生每行星号构成的向量,然后利用foreach函数循环输出
-
利用双重循环与流间变量,这个是Scala语言特有的东西
任务2、遍历字符串,输出每个字符
-
方法一、按索引取字符串的每个字符(传统for循环)
-
方法二:将字符串看作一个由多个字符组成的集合(增强for循环)
任务3、计算 1 + 2 + 3 + … + 100
- 注意
sum
必须定义为var
型变量 - 利用集合的归并方法来求和最简单
任务4、输出列表内的偶数(过滤)
-
采用三种方法来实现
任务5、输出全部两位素数
1:for (n <- 10 to 100; if !(n % 2 == 0 || n % 3 == 0 || n % 5 == 0 || n % 7 == 0)) print(n.toString + " ")
2;(10 to 100).filter(n => !(n % 2 == 0 || n % 3 == 0 || n % 5 == 0 || n % 7 == 0))
(二)嵌套for循环
1、语法格式
- 传统格式
for (变量1 <- 集合或数组(条件)) for (变量2 <- 集合或数组(条件)) { 语句组 } }
- 特有格式
for (变量1 <- 集合或数组; 变量2 <- 集合或数组 (条件)) { 语句组 }
2、案例演示
- 在
net.cl.day03
包里创建Example02
对象(Example01在后面,顺序不对,注意看清楚)package net.cl.day03 object Example02 { /** * 功能:双重循环打印九九表 * 作者:cl * 日期:2023年02月27日 */ def main(args: Array[String]): Unit = { for (i <- 1 to 9) { for (j <- 1 to i) { print(i.toString + "×" + j + "=" + (i * j) + "\t") } println() } } }
-
运行程序,查看结果
方法二、采用单重循环来实现
- 在
net.cl.day03
包里创建Example03
对象package net.cl.day03 object Example03 { /** * 功能:单重循环打印九九表 * 作者:cl * 日期:2023年02月27日 */ def main(args: Array[String]): Unit = { for (i <- 1 to 9; j <- 1 to i) { print(i.toString + "×" + j + "=" + (i * j) + "\t") if (j == i) println() } } }
-
运行程序,查看结果
方法三、采用单重循环与流间变量绑定来实现
package net.cl.day03
object Example04 {
/**
* 功能:单重循环与流间变量绑定打印九九表
* 作者:cl
* 日期:2023年02月27日
*/
def main(args: Array[String]): Unit = {
for (i <- 1 to 9; j <- 1 to i; sep = if (j == i) "\n" else "\t") {
print(i.toString + "×" + j + "=" + (i * j) + sep)
}
}
}
- 在
net.cl.day03
包里创建Example04
对象 - 在for循环头里
sep = if (i == j) "\n" else "\t"
就是流间变量绑定 - 运行程序,查看结果
方法四、采用单重循环、流间变量与yield来实现
for循环语句本身的返回值是Unit类型,无论在循环体中返回什么都是无效的,最终得到的都是Unit的值,但是可以在循环中的循环条件和循环体之间加上yield关键字,那么就可以将循环体产生的返回值组成数组进行返回。
在net.cl.day03包里创建Example01对象
package net.cl.day03
object Example01 {
/**
* 功能:采用单重循环、流间变量与yield打印九九表
* 作者:cl
* 日期:2023年02月27日
*/
def main(args: Array[String]): Unit = {
val list = for (i <- 1 to 9; j <- 1 to i; sep = if (j == i) "\n" else "\t")
yield i.toString + "×" + j + "=" + (i * j) + sep
list.foreach(print)
}
}
运行结果如下:
任务2、去掉对角线
- 一个三阶方阵,单元格的值是行号与列号的乘积,去掉对角线,输出剩余元素
- 方法一、传统双重循环(创建Example05对象)
package net.cl.day03 object Example05 { /** * 功能:去掉对角元素 * 作者:cl * 日期:2023年02月27日 */ def main(args: Array[String]): Unit = { for (i <-1 to 3) { for (j <- 1 to 3) { if (i != j) print((i * j).toString + "\t") else print("\t") } println() } } }
-
运行程序,查看结果
-
方法二、特有双重循环
package net.cl.day03 object Example07 { /** * 功能:去掉对角线元素(双重循环) * 作者:cl * 日期:2023年03月02日 */ def main(args: Array[String]): Unit = { for (i <- 1 to 3; j <- 1 to 3; sep = if (i == j) "\t" else (i * j).toString + "\t") { print(sep) if (j == 3) println() } } }
-
运行程序,查看结果
-
方法三:采用两个流间变量和yield
package net.cl.day03 object Example08 { /** * 功能:去掉对角线元素 * 作者:cl * 日期:2023年03月02日 */ def main(args: Array[String]): Unit = { val list = for (i <- 1 to 3; j <- 1 to 3; sep = if (j==3) "\n" else "\t"; str = if (i != j) (i * j).toString + sep else " " + sep) yield str list.foreach(print) } }
-
运行程序,查看结果
四、条件循环
(一)while循环
1、语法格式
- Scala的while循环与Java类似
while(条件) {
循环体
}
2、案例演示
-
在
net.cl.day03
包里创建Example10
对象 - 采用函数式风格来写代码,递归函数来实现求和,注意:此处
sum
是常量 - 在
net.huawei.day02
包里创建Example10_
对象package net.cl.day03 object Example10 { /** * 功能:等差数列求和 * 作者:cl * 日期:2023年03月02日 */ def mx(n: Int, sum: Int): Int = { if (n > 0) mx(n - 1, sum + n) else sum } def main(args: Array[String]): Unit = { println("1 + 2 + 3 + ... + 100 = " + mx(100, 0)) } }
-
运行程序,查看结果
-
普通版:
package net.cl.day03 object Example12 { /** * 功能:等差数列求和 * 作者:cl * 日期:2023年03月02日 */ def main(args: Array[String]): Unit = { var sum = 0 var i = 1 do { sum = sum + i i = i + 1 } while (i <= 100) println("1 + 2 + 3 + ... + 100 = " + sum) } }
-
运行程序,结果如下
任务2、打印全部水仙花数
- 所谓水仙花数,是指等于其各位数字立方和的三位数。
-
首先求n的个位数:n % 10
然后要将三位数变成两位数:n = n / 10;
对于新的两位数n,又求它的个位数:n % 10
然后要将两位数变成一位数:n = n / 10; -
在
net.cl.day02
包里创建Example11
对象package net.cl.day03 object Example11 { /** * 功能:打印水仙花数 * 作者:cl * 日期:2023年03月02日 */ def main(args: Array[String]): Unit = { for (n <- 100 to 999) { val p1 = n % 10 // 个位数 val p2 = n / 10 % 10 // 十位数 val p3 = n / 100 // 百位数 if (n == p1 * p1 * p1 + p2 * p2 * p2 + p3 * p3 * p3) println(n.toString + " = " + p3 + "^3 + " + p2 + "^3 + " + p1 + "^3") } } }
-
运行程序,查看结果
五、异常处理
(一)异常处理概述
- Scala中继承了Java的异常机制,提供了程序中产生意外情况时处理的机制,抛出异常的过程和Java中基本一致,通过
throw
关键字进行:throw XxxException()
,一旦抛出可以当场捕获处理或接着向上抛,捕获异常是通过try-catch-finally
来实现的。
(二)案例演示
任务:演示try-catch-finally
try-catch-finally
是有返回值的:如果没有异常就是try
语句的返回值,如果有异常就是catch
语句的返回值。注意不会是finally
的返回值,finally
即使有返回值,也会被抛弃,这点和Java是不同的。- 在
net.cl.day03
包里创建Example13
对象package net.cl.day03 import java.io.IOException /** * 功能:演示异常处理 * 作者:cl * 日期:2023年03月02日 */ object Example13 { def main(args: Array[String]): Unit = { var message = "" val result = try { mx() // 调用方法,会抛出异常 "恭喜,程序执行正常~" } catch { case e: NullPointerException => "空指针异常" case e: IOException => "呵呵,这是I/O异常~" case e: RuntimeException => "哈哈,这是运行时异常~" case e: Exception => "管它呢,反正是异常~" } finally { message = "程序到此为止~" "无论是否有异常,都会执行finally里的语句~" } println(result) println(message) } def mx(): Unit = { throw new RuntimeException("故意抛出一个运行时异常~")//注释掉mx()方法李的语句就可以运行成功 } }
-
执行程序,查看结果(此时有异常,result取的是catch里的返回值 -
哈哈,这是运行时异常~
,finally语句块执行了的,因此message可以打印出来 -程序到此为止!
) -
注释掉
mx()
方法里的语句 -
执行程序,查看结果(此时有异常,result取的是try里的返回值 -
恭喜,程序执行正常!
)
(二)案例演示
任务:给城市下评语
- 在
net.cl.day03
包里创建Example14
对象package net.cl.day03 import scala.io.StdIn /** * 功能:给城市下评语 * 作者:cl * 日期:2023年03月02日 */ object Example14 { def main(args: Array[String]): Unit = { print("输入城市:") val city = StdIn.readLine() val comment = city match { case "北京" => "是伟大的首都~" case "上海" => "是神奇的魔都~" case "广州" => "是迷人的妖都~" case "乐山" => "是甜皮鸭的家~" case _ => "是普通的城市~" } println(city + comment) } }
七、补充案例
任务:评定成绩等级
1、编写符合函数式编程风格的Sala程序
- 在
net.cl.day03
包里创建Example17
对象package net.cl.day03 import scala.io.StdIn /** * 功能:成绩等级评定(采用函数式风格) * 作者:cl * 日期:2023年03月02日 */ object Example17 { def main(args: Array[String]): Unit = { print("score = ") val score = StdIn.readLine().toInt val comment = if (score > 100) { "超出范围" } else if (score >= 90) { "优秀" } else if (score >= 80) { "良好" } else if (score >= 70) { "中等" } else if (score >= 60) { "及格" } else if (score >= 0) { "不及格" } else { "超出范围" } println("评语:" + comment) } }
-
运行程序,查看结果
-
函数式风格的程序有几个特点
if结构像函数一样有返回值
if结构里除了传入参数score之外,没有别的变量。
-if结构里没有与外界交流,比如输入或输出或网络连接或读取文件之类。
函数式编程是为了处理计算,不考虑系统的读写(I/O)。
函数式编程强调没有副作用(指的是函数内部与外部互动,产生运算以外的其它结果)。函数要求独立,只返回一个值,没有其它行为,尤其不能修改外部变量的值
2、非函数式风格的程序
package net.cl.day03
import scala.io.StdIn
/**
* 功能:成绩等级评定(采用非函数式风格)
* 作者:cl
* 日期:2023年03月02日
*/
object Example18 {
def main(args: Array[String]): Unit = {
print("score = ")
val score = StdIn.readLine().toInt
var comment = ""
if (score > 100) {
comment = "超出范围"
} else if (score >= 90) {
comment = "优秀"
} else if (score >= 80) {
comment = "良好"
} else if (score >= 70) {
comment = "中等"
} else if (score >= 60) {
comment = "及格"
} else if (score >= 0) {
comment = "不及格"
} else {
comment = "超出范围"
}
println("评语:" + comment)
}
}
虽然处理结果跟上面那个程序一样,但是并不符合函数式编程风格。因为if结构有副作用,修改了if结构之外的变量comment的值
package net.cl.day03
import scala.io.StdIn
/**
* 功能:成绩等级评定(采用非函数式风格)
* 作者:华卫
* 日期:2023年03月01日
*/
object Example19 {
def main(args: Array[String]): Unit = {
print("score = ")
val score = StdIn.readLine().toInt
if (score > 100) {
println("评语:超出范围")
} else if (score >= 90) {
println("评语:优秀")
} else if (score >= 80) {
println("评语:良好")
} else if (score >= 70) {
println("评语:中等")
} else if (score >= 60) {
println("评语:及格")
} else if (score >= 0) {
println("评语:不及格")
} else {
println("评语:超出范围")
}
}
}
不符合函数式编程风格。因为if结构有副作用,有写操作