只有if, while, for, try, match
. 更多在库中. 只有while
无返回值.
命令式风格:
var 文件名 = "默认.txt"
if (!参数.isEmpty)
文件名 = 参数[0]
改为if, 也避免了var:
val 文件名 =
if (!参数.isEmpty) 参数[0]
else "默认.txt"
这样某些情况下可以省去变量:
println(if (!参数.isEmpty) 参数[0] else "默认.txt")
建议尽量用val, 方便阅读也易于重构
def 最大公约数循环(x: Long, y: Long): Long = {
var a = x
var b = y
while (a != 0) {
val 临时 = a
a = b % a
b = 临时
}
b
}
do-while循环读行至行为空:
var 行 = ""
do {
行 = readLine()
println("读: " + 行)
} while (行 != "")
while结构不是表达式, 因为它们只有Unit
返回值. 下面演示它与void
的不同:
scala> def 问好() = { println("嗨") }
问好: ()Unit
scala> () == 问好()
<console>:13: warning: comparing values of types Unit and Unit using `==' will always yield true
() == 问好()
^
嗨
res0: Boolean = true
下面的赋值也返回unit值:
var 行 = ""
while ((行 = readLine()) != "") // () != "" 总为真, 因此无限循环
println("读: " + 行)
上一章的最大公约数
是递归实现, 建议尽量用它代替while循环. while循环中也常见var的使用, 或I/O.
遍历
val 此处文件 = (new java.io.File(".")).listFiles
for (文件 <- 此处文件)
println(文件)
遍历i从1到4
scala> for (i <- 1 to 4)
| println("迭代" + i)
迭代1
迭代2
迭代3
迭代4
不包括上限:
scala> for (i <- 1 until 4)
| println("迭代" + i)
迭代1
迭代2
迭代3
第一个文件遍历例子也可这样写, 但不常见:
for (i <- 0 until 此处文件.length)
println(此处文件(i))
因为并不需要i, 也更易于出错
过滤
val 此处文件 = (new java.io.File(".")).listFiles
for (文件 <- 此处文件 if 文件.getName.endsWith(".scala"))
println(文件)
等同于:
val 此处文件 = (new java.io.File(".")).listFiles
for (文件 <- 此处文件)
if (文件.getName.endsWith(".scala"))
println(文件)
也支持多个过滤条件:
for (
文件 <- 此处文件
if 文件.isFile
if 文件.getName.endsWith(".scala")
) println(文件)
嵌套迭代 下面等价于两层for遍历
def 从文件读行(文件: java.io.File) =
scala.io.Source.fromFile(文件).getLines.toList
def 匹配文本(模式: String) =
for (
文件 <- 此处文件
if 文件.getName.endsWith(".scala");
行 <- 从文件读行(文件)
if 行.trim.matches(模式)
) println(文件 + ": " + 行.trim)
匹配文本(".*gcd.*")
临时(Mid-stream)变量绑定 上面例子中.trim调用两次. 下面用一个变量避免, 前面省略val
为何用{}? -- 因为有额外的赋值语句
def 从文件读行(文件: java.io.File) =
scala.io.Source.fromFile(文件).getLines.toList
def 匹配文本(模式: String) =
for {
文件 <- 此处文件
if 文件.getName.endsWith(".scala");
行 <- 从文件读行(文件)
已修剪 = 行.trim
if 已修剪.matches(模式)
} println(文件 + ": " + 已修剪)
匹配文本(".*gcd.*")
生成新集合
def scala文件 =
for {
文件 <- 此处文件
if 文件.getName.endsWith(".scala")
} yield 文件
注意格式为for 子句 yield 块
. 不能将yield置于for之内
上一节的例子改为生成int数组:
val for行长度 =
for {
文件 <- 此处文件
if 文件.getName.endsWith(".scala");
行 <- 从文件读行(文件)
已修剪 = 行.trim
if 已修剪.matches(".*for.*")
} yield 已修剪.length
抛异常
throw new IllegalArgumentException
throw的返回类型为Nothing
, 详见11.3
val 一半 =
if (n % 2 == 0)
n / 2
else
throw new RuntimeException("n需为偶数")
捕捉异常
import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException
try {
val 文件 = new FileReader("输入.txt")
} catch {
case 异常: FileNotFoundException => // 处理无此文件情况
case 异常: IOException => // 处理其他I/O错误
}
注意与Java不同, Scala不需用throws
声明所有异常
finally子句
import java.io.FileReader
val 文件 = new FileReader("输入.txt")
try {
// 使用文件
} finally {
文件.close()
}
loan模式见9.4
产生一个值 catch可有返回值:
import java.net.URL
import java.net.MalformedURLException
def 取url(路径: String) =
try {
new URL(路径)
} catch {
case e: MalformedURLException =>
new URL("某路径")
}
finally如果显式返回一个值, 将会覆盖try或catch中返回的值, f()返回2:
def f(): Int = try return 1 finally return 2
如果隐式返回一个值, 则不会覆盖, g()返回1:
def g(): Int = try 1 finally 2
由于不很想当然, 建议finally仅用于善后的副作用操作, 如关闭文件
基本功能类似于switch, 但match可用户匹配任意模式(详见15章)
val 首参数 = if (参数.length > 0) 参数[0] else ""
首参数 match {
case "甜酱" => println("辣酱")
case "羊肉" => println("泡馍")
case "皮蛋" => println("豆腐")
case _ => println("啥?")
}
与swtich的最大区别是, match有返回值:
val 首参数 = if (参数.length > 0) 参数[0] else ""
val 伙伴 =
首参数 match {
case "甜酱" => println("辣酱")
case "羊肉" => println("泡馍")
case "皮蛋" => println("豆腐")
case _ => println("啥?")
}
println(伙伴)
Java例程:
int i = 0;
boolean 找到了 = false;
while (i < 参数.length) {
if (参数[i].startsWith("-")) {
i = i + 1;
continue;
}
if (参数[i].endsWith(".scala")) {
找到了 = true;
break;
}
i = i + 1;
}
对应Scala:
var i = 0
var 找到了 = false
while (i < 参数.length && !找到了) {
if (!参数(i).startsWith("-")) {
if (参数(i).endsWith(".scala"))
找到了 = true
}
i = i + 1
}
如想避免var, 用递归:
def 开始搜(i: Int): Int =
if (i >= 参数.length) -1
else if (参数(i).startsWith("-")) 开始搜(i + 1)
else if (参数(i).endsWith(".scala")) i
else 开始搜(i + 1)
val i = 开始搜(0)
尾递归优化详见8.9
如果一定要使用break, 有scala.util.control提供
def 乘法表() = {
var i = 1
// 仅i
while (i <= 10) {
var j = 1
// i和j
while (j <= 10) {
var 乘积 = (i * j).toString
// i, j, 乘积
var 长度 = 乘积.length
// i, j, 乘积, 长度
while (长度 < 4) {
print(" ")
长度 += 1
}
print(乘积)
j += 1
}
// i, j; 乘积, 长度已出作用域
println()
i += 1
}
// 仅i
}
与Java一个不同是, 允许在嵌套作用域中使用同名变量, 下面先打印2后打印1:
val a = 1;
{
val a = 2
println(a)
}
println(a)
交互环境中, 每个声明都在一个新嵌套的作用域中:
scala> val a = 1
a: Int = 1
scala> val a = 2
a: Int = 2
scala> println(a)
2
可看成:
val a = 1;
{
val a = 2
{
println(a)
}
}
改为函数式风格:
def 创建行序列(行: Int) =
for (列 <- 1 to 10) yield {
val 乘积 = (行 * 列).toString
val 缩进 = " " * (4 - 乘积.length)
缩进 + 乘积
}
def 创建行(行: Int) = 创建行序列(行).mkString
def 乘法表() = {
val 表序列 =
for (行 <- 1 to 10)
yield 创建行(行)
表序列.mkString("\n")
}
(第七章完)