Scala流程控制
Scala 内建的控制结构屈指可数 ,仅有 if 、while 、for , try 、match 和语句块而已。如此之少的理由是,Scala 从语法层而支持函数字面面量。因此 ,代之以在基本语法之上逐一添加高级的内建控制结构,Scala 可以把它们实现在函数库中。
可能你已经发现 ,几乎所有的 Scala的控制结构都会产生某个值。这是函数式语言所采用的方式, 程序被看成是计算值的活动,因此程序的各个部件也不应例外。你也可以把这种方式看做指令式语言发展趋势的逻辑推演,函数调用可以返回值 ( 或者更新被当作参数传入的输出变量 )。另外,指令式语言还有三元操作符 ,其行为与 if 一致,但结果却是值。Scala 采用了这种三元操作符模型 ,但仍称为 if。换句话说,Scala 的 if 可以产生值。于是 Scala 让这种趋势继续发展 使得 for , try 和 match 也产生值。
程序员能够用结果值简化代码 ,就如同用函数的返回值那样。如果没有这种机制,程序员就必须创建临时变量来保存控制结构中的汁算结果。去掉这些临时变量能让代码更简洁并避免类似于多个分支内变量设置不一致而导致的问题。
总而言之,Scala 的基础控制结构虽然少,但也足以支将指令式语言里所有的实质内容。进一步说 由于它们都能产生结果值 ,因此有助于缩短代码。
下面总结Scala的流程控制语句为:判断结构、选择结构、循环结构、语句块、异常控制。
1.判断结构
if 语句简介:
Scala的if/else语法结构和Java或C++一样。不过,在Scala中if/else表达式有值 ,这个值就是跟在if或else之后的表达式的值。例如:
if ( x > 0) 1 else -1
上述表达式的值是1或 -1 ,具体是哪一个取决于x的值。
你可以将if/else表达式的值 赋值给变量 :
val s = if ( x > 0) 1 else -1
这与如下语句的效果一样:
if ( x > 0 ) s = 1 else s = -1
不过 ,第一种写法更好 ,因为它可以用来初始化一个val 、而在第二种写法当中 ,s必须是var
Java和C++有一个?:操作符用于同样目的。
如下表达式
x > 0 ? 1 : -1 // Java或C++
等同于Scala表达式 if (x > 0) 1 else -1 不过 ,你不能在?:表达式中插入语句。Scala的if/else将在Java和C++中分开的两个语法结构if/else和?:结合在了一起。
在Scala中,每个表达式都有一个类型。举例来说,表达式if (x > 0) 1 else -1 的类型是Int ,因为两个分支的类型都是Int。混合类型表达式,比如:
if ( x > 0 ) "positive" else - 1
上述表达式的类型是两个分支类型的公共超类型。在本例中,其中一个分支是:java.lang.String,而另一个分支是Int。它们的公共超类型叫做Any。
如果else部分缺失了 ,比如:
if (x > 0) 1
那么有可能if语句没有输出值。但是在Scala中,每个表达式都应该有某种值。这个问题 的解决方案是引人一个Unit类: ,写做()。不带else的这个if语句等同于
if (x > 0) 1 else ()
你可以把()当做是表示 “无有用值” 的占位符 ,将Unit 当做Java或C++中的void。
(从技术上讲,void没有值但是Unit有一个表示 “无值” 的值。如果你一定要深究的 话,这就好比空的钱包和里面有一张写着 “没钱” 的元面值钞票的钱包之间的区别。)
if语句常用结构
if 语句:
if 语法结构:
if(布尔表达式){
<span style="white-space:pre"> </span>语句块 // 如果布尔表达式为true 则执行该语句块
}
如果布尔表达式为 true 则执行大括号内的语句块,否则跳过大括号内的语句块,执行大括号之后的语句块。
实例
object Test {
def main(args: Array[String]) {
var x = 10
if (x < 20) println("x < 20")
}
}
if...else 语句
if 语句后可以紧跟 else 语句,else 内的语句块可以在布尔表达式为 false 的时候执行。
if...else 的语法结构:
if(布尔表达式){
<span style="white-space:pre"> </span>表达式1 // 如果布尔表达式为 true 则执行该语句块
}else{
<span style="white-space:pre"> </span>表达式2 // 如果布尔表达式为 false 则执行该语句块
}
实例
object Test {
def main(args: Array[String]) {
var x = 30
if (x < 20) {
println("x 小于 20")
} else {
println("x 大于 20")
}
}
}
if...else if...else 语句
if 语句后可以紧跟 else if...else 语句,在多个条件判断语句的情况下很有用。
if...else if...else 语法结构:
if(布尔表达式 1){
执行语句 // 如果布尔表达式 1 为 true 则执行该语句块
}else if(布尔表达式 2){
执行语句 // 如果布尔表达式 2 为 true 则执行该语句块
}else if(布尔表达式 3){
执行语句 // 如果布尔表达式 3 为 true 则执行该语句块
}else {
执行语句 // 如果以上条件都为 false 执行该语句块
}
实例
object Test {
def main(args: Array[String]) {
var x = 30;
if (x == 10) {
println("X 的值为 10")
} else if (x == 20) {
println("X 的值为 20")
} else if (x == 30) {
println("X 的值为 30")
} else {
println("无法判断 X 的值")
}
}
}
if...else 嵌套语句
if...else 嵌套语句可以实现在 if 语句内嵌入一个或多个 if 语句。
if...else 嵌套语句语法结构:
if(布尔表达式 1){
执行语句 // 如果布尔表达式 1 为 true 则执行该语句块
if(布尔表达式 2){
执行语句 // 如果布尔表达式 2 为 true 则执行该语句块
}
}
实例
object Test {
def main(args: Array[String]) {
var x = 30
var y = 10
if (x == 30) {
if (y == 10) {
println("X = 30 , Y = 10")
}
}
}
}
2.选择结构
Scala 的match 表达式类似于其他语言中的 switch语句 ,它可以提供给你在多个备选项作选择。基本上match 表达式可以让你使用任意的模式(pattern)作选择 。Scala的match表达式十分强大,以后作详细讲解。目前,只需了解使用match在若干备选项中做选择就可以了。
与 Java的switch 相比 ,Scala 最显著的差别或许是match 表达式也能产生值。
程序示例:
object Test {
def main(args: Array[String]): Unit = {
val firstArg = if (!args.isEmpty) args(0) else ""
val friend = //match表达式的返回值
firstArg match {
case "salt" => "pepper"
case "chips" => "salsa"
case "eggs" => "bacon"
case _ => "huh?"
}
println(friend)
}
}
3.循环结构
有的时候,我们可能需要多次执行同一块代码。一般情况下,语句是按顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。
循环语句允许我们多次执行一个语句或语句组。Scala语言提供了三种循环类型while循环、do...while循环、for循环。
while循环
//Scala拥有与Java和C++相同的while和do while循环。例如
while (n > 0) {
r = r * n
n -= 1
}
do...while循环
//Scala拥有与Java和C++相同的while和do while循环。例如
do {
r = r * n
n -= 1
} while (n > 0)
for循环
Scala 语言中 for 循环的语法:
for( x <- Range ){
//statement(s);
}
以上语法中,Range 可以是一个数字区间表示 i to j ,或者 i until j。左箭头 <- 用于为变量 x 赋值。
- i to j 表示的集合为两边都是闭区间:[i ,j] 例:1 to 10 表示 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
- i until j 表示的集合为左闭区间,右开区间: 例:1 until 10 表示 1, 2, 3, 4, 5, 6, 7, 8, 9
程序示例:
for( x <- 1 to 10 ){
print(x+", ")
}
打印的结果为:1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
for 循环过滤
Scala 可以使用一个或多个 if 语句来过滤一些元素。
以下是在 for 循环中使用过滤器的语法。
你可以以 (变量 <- 表达式) 的形式提供多个生成器 ,用分号将它们隔开。例如:
for (i <- 1 to 3; j <- 1 to 3) print((10 * i + j) + " ")
//将打印 11 12 13 2 1 22 23 31 32 33
每个生成器都可以带一个守卫 ,以if开头的Boolean表达式。例如:
for (i <- 1 to 3; j <- 1 to 3 if i != j) print((10 * i + j) + " ")
//将打印 12 13 21 23 31 32
注意在if之前并没有分号。 你可以使用任意多的定义,引入可以在循环中使用的变量:
for (i <- 1 to 3; from = 4 - i; j <- from to 3) print((10 * i + j) + " ")
//将打印 13 22 23 31 32 33
如果for循环的循环体以yield开始 ,则该循环会构造出一个集合 ,每次迭代生成集 合中的一个值:
for (i <- 1 to 10) yield i % 3
//生成 Vector ( 1 , 2 , 0 , 1, 2 , 0 , 1, 2 , 0, 1)
for (c <- "Hello"; i <- 0 to 1) yield (c + i).toChar
//将生成 "HIeflmlmop"
for循环枚举
枚举:
for (
file <- filesHere
if file.isFile;
if file.getName.endsWith(".scala")
)
println(file)
注意:
如果在发生器中加入超过一个过滤器,if子句必须用分号分隔,这也是上面例子中”if file.isFile”过滤器之后带分号的原因。
嵌套枚举:
def fileLines(file: java.io.File) = scala.io.Source.fromFile(file).getLines.toList
def grep(pattern: String) = for (
file <- filesHere
if file.getName.endsWith(".scala")
line <- fileLines(file)
if line.trim.matches(pattern)
) println(file + ":" + line.trim)
grep(".*gcd.*")
4.语句块
在Java或C++中,块语句是一个包含于{ }中的语句序列。每当你需要在逻辑分支或 循环中放置多个动作时 ,你都可以使用块语句。
在Scala中,{}块包含一系列表达式 ,其结果也是一个表达式 。块中最后一个表达式的值就是块的值。
这个特性对于那种对某个val的初始化需要分多步完成的情况很有用。例如
val distance = { val dx = x - x0; val dy = y - y0; sqrt(dx * dx + dy * dy) }
{ }块的值取其最后一个表达式 ,在此处以粗体标出。变量dx和dy仅作为计算所需 要的中间值,很干净地对程序其他部分而言不可见了。
5.异常控制
使用try表达式处理异常
Scala 的异常和许多其他语言的一样。方法除了能以通常的为式返回值以外,还可以通过抛出异常中止
执行。方法的调用者要么可以擒获并处理这个异常.或者也可以只是简单地中止掉,并把异常上升到调用者 的调用者处。异常以这种方式上升,逐层释放调用堆钱,直到某个方法接受处理或不再剩下其他的方法。
val half =
if (n % 2 == 0)
n / 2
else
throw new RuntimeException("n must be even")
这段代码的意思是,如果 n 是偶数、half 将被初始化为 n 的一半。如果 n 不是偶数 .那么异常将 在 half 被初始化为任何值之前被抛出。因此,无论怎么说,把抛出的异常当作任何类型的值都是安全的。任何使用经 throw 返回值的尝试都不会起作用,因此这样做不会有害处。
捕获异常
Scala捕获异常的语法采用的是模式匹配的语法
try {
process(new URL("http://horstman.com/fred-tiny.gif"))
} catch {
case _: MalformedURLException => println("Bad URL: " + url)
case ex: IOException => ex.printStackTrace()
}
和Java或C++一样 ,更通用的异常应该排在更具体的异常之后。 注意,如果你不需要使用捕获的异常对象,可以使用 _ 来替代变量名。 try / finally语句让你可以释放资源 ,不论有没有异常发生。例如
val in = new URL("http://horstman.com/fred.gif").openStream()
try {
prcess(in)
} finally {
in.close()
}
finally语句不论process函数是否抛出异常都会执行,reader总会被关闭
6.不再使用break和continue
说明:Scala并没有提供break或continue语句来退出循环。那么如果需要break时我们该怎么做呢?有如下几个选项:
1. 使用Boolean型的控制变量 。
Java中:
int i = 0;
boolean foundIt = false
while (i < args.length) {
if (args[i].startWith("-")) {
i = i + 1;
continue;
}
if (args[i].endsWith(".scala")) {
foundIt = true;
break;
}
i = i + 1;
}
Scala中:
var i = 0
var foundIt = false
while (i < args.length && !foundIt) {
if (!args(i).startsWith("-")) {
if (args(i).endsWith(".scala"))
foundIt = true
}
i = i + 1
}<span style="color: rgb(51, 51, 51); font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
2. 使用嵌套函数————你可以从函数当中return 。
3. 使用Breaks对象中的break方法:
import scala.util.control.Breaks._
breakable {
for (....)
{
if (...) break; //退出breakble块
...
}
}
在这里 ,控制权的转移是通过抛出和捕获异常完成的,因此,如果时间很重要的话,你应该尽量避免使用这套机制。