if 表达式
Scala 的 if 如同许多其它语言中的一样工作。它测试一个状态并据其是否为真,执行两个分支中的一个:
var filename = "default.txt"
if (!args.isEmpty)
filename = args(0)
由于 Scala 的 if 是能返回值的表达式,可以改成用 val 的更函数式的风格:
val filename =
if (!args.isEmpty) args(0)
else "default.txt"
使用 val 而不是 var 的第二点好处是他能更好地支持等效推论:equational reasoning。在表达式没有副作用的前提下,引入的变量等效于计算它的表达式。因此,无论何时都可以用表达式替代变量名:
println(if (!args.isEmpty) args(0) else "default.txt")
while 循环
Scala 的 while 循环表现的和在其它语言中一样。包括一个状态和循环体,只要状态为真,循环体就一遍遍被执行:
def gcdLoop(x: Long, y: Long): Long = {
var a = x
var b = y
while (a != 0) {
val temp = a
a = b % a
b = temp
}
B
}
Scala 也有 do-while 循环。除了把状态测试从前面移到后面之外,与 while 循环没有区别。
While 和 do-while 结构被称为“循环”,不是表达式,因为它们不产生有意义的结果,结果的类型是 Unit 。说明产生的值的类型为 Unit 。被称为 unit value ,写做 () 。 () 的存在是 Scala 的 Unit 不同于 Java 的 void 的地方:
cala> def greet() { println("hi") }
greet: ()Unit scala> greet() == ()
hi
res0: Boolean = true
由于方法体之前没有等号, greet 被定义为结果类型为 Unit 的过程。因此, greet 返回 unit 值 () 因此在比较 greet 的结果和 unit 值 () ,的相等性,产生true。
for 表达式
枚举集合类
用 for 做的最简单的事情就是把一个集合类的所有元素都枚举一遍:
val filesHere = (new java.io.File(".")).listFiles
for (file <- filesHere)
println(file)
代码先创建指向当前目录 “.” 的文件,调用 listFiles 方法,返回 File 对象数组保存在 filesHere 变量中,通过发生器:generator的语法“file <- filesHere”遍历了 filesHere 的元素,每一次枚举 file 的新 val 就被元素值初始化并被打印出来。
for 表达式语法对任何种类的集合类都有效而不只是数组,可以使用类似于 “1 to 5” 这样的语法创建一个 Range ,然后用 for 枚举:
scala> for (i <- 1 to 4)
| println("lteration" + i)
lteration1
lteration2
lteration3
lteration4
过滤
有些时候不想枚举一个集合类的全部元素。而是想过滤出一个子集。可以通过把过滤器:filter:一个 if 子句加到 for 的括号里做到:
val filesHere = (new java.io.File(".")).listFiles
for (file <- filesHere if file.getName.endsWith(".scala"))
println(file)
也可以这么写:
for (file <- filesHere)
if (file.getName.endsWith(".scala"))
println(file)
如果愿意的话,可以包含更多的过滤器。只要不断加到子句里即可:
for (
file <- filesHere
if file.isFile;
if file.getName.endsWith(".scala")
)println(file)
如果在发生器中加入超过一个过滤器, if 子句必须用分号分隔。
嵌套枚举
如果加入多个 <- 子句就得到嵌套的“循环”:
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.*")
代码展示的 for 表达式有两个嵌套循环,外层循环枚举 filesHere ,内层的枚举所有以 .scala 结尾的文件的 fileLines(file) 。可以使用大括号代替小括号环绕发生器和过滤器,这样的好处是可以省略一些使用小括号必须加的分号。
mid-stream (流间) 变量绑定
注意到前段代码中重复出现的表达式 line.trim ,这不是个可忽略的计算,如果想每次只算一遍,可以用等号 (=) 把结果绑定到新变量做到这点,绑定的变量被当做用 val 引入和使用,不过不用带关键字 val :
def grep(pattern: String) =
for {
file <- filesHere
if file.getName.endsWith(".scala")
line <- fileLines(file)
trimmed = line.trim
if trimmed.matches(pattern)
} println(file + ": " + trimmed)
grep(".*gcd.*")
名为 trimmed 的变量被引入 for 表达式,并被初始化为 line.trim 的结果值。之后的 for 表达式就可以在两个地方使用这个新变量,一次在 if 中,一次在 println 中。
制造新集合
可以创建一个值去记住每一次的迭代,只要在 for 表达式之前加上关键字 yield :
def scalaFiles =
for {
file <- filesHere
if file.getName.endsWith(".scala")
} yield file
for 表达式在每次执行的时候会制造一个值,当 for 表达式完成的时候,结果将是一个包含了所有产生的值的集合,结果集合的类型基于枚举子句处理的集合类型。 for-yield 表达式的语法:
for {子句} yield {循环体}
yield 在整个循环体之前,即使循环体是一个被大括号包围的代码块,也一定把 yield 放在左括号之前而不是代码的最后一个表达式之前:
for (file <-filesHere if file.getName.endsWith(".scala")) {
yield file // 语法错误!
}
使用 try 表达式处理异常
抛出异常
异常的抛出看上去与 Java 的一模一样。首先创建一个异常对象然后用 throw 关键字抛出:
throw new IllegalArgumentException
Scala 里, throw 也是有结果类型的表达式,抛出异常的类型是 Nothing ,尽管 throw 不实际得出任何值,但还是可以把它当做表达式。
捕获异常
用来捕获异常的语法如下:
import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException
try {
val f = new FileReader("input.txt")
// Use and close file
} catch {
case ex: FileNotFoundException => // Handle missing file
case ex: IOException => // Handle other I/O error
}
这个 try-catch 表达式的行为与其它语言中的异常处理一致。程序体被执行,如果抛出异常,每个 catch 子句依次被尝试。本例中,如果异常是 FileNotFoundException ,那么第一个子句将被执行。如果是 IOException 类型,第二个子句将被执行。如果都不是,那么try-catch将终结并把异常上升出去。
finally 子句
和其它大多数 Scala 控制结构一样, try-catch-finally 也产生值。 Scala 的行为与 Java 的差别仅源于 Java 的 try-finally 不产生值。 Java 里如果 finally 子句包含一个显式返回语句,或抛出一个异常,这个返回值或异常将“凌驾”于任何之前源于 try 代码块或某个它的 catch 子句产生的值或异常之上:
def f(): Int = try { return 1 } finally { return 2 }
调用 f() 产生结果值 2 ,相反:
def g(): Int = try { 1 } finally { 2 }
调用 g() 产生 1 。
match 表达式
Scala 的匹配表达式允许在许多可选项:alternative中做选择,就好象其它语言中的 switch 语句。 Match 表达式可以你使用任意的模式:pattern做选择:
val firstArg = if (args.length > 0) args(0) else ""
firstArg match {
case "salt" => println("pepper")
case "chips" => println("salsa")
case "eggs" => println("bacon")
case _ => println("huh?")
}
match 缺省情况用下划线 (_) 说明,这是常用在 Scala 里作为占位符表示完全不清楚的值的通配符。
Scala 里的 case 匹配表达式可以使任何种类的常量,每个可选项最后没有 break ,但是 break 是隐含的。 match 表达式也能产生值:
val firstArg = if (!args.isEmpty) args(0) else ""
val friend =
firstArg match {
case "salt" => "pepper"
case "chips" => "salsa"
case "eggs" => "bacon"
case _ => "huh?"
}
println(friend)
变量范围
Scala 程序里的变量定义有一个能够使用的范围:scope。大括号通常引入了一个新的范围,任何定义在大括号里的东西在括号之后就脱离了范围。
一旦变量被定义了就不能在同一范围内定义同样的名字,但是可以在内部范围内定义与外部范围内名称相同的变量,用大括号括起来即为内部范围。内部变量会遮蔽同名的外部变量。