Scala具有你期望在编程语言中找到的基本控制结构,包括:
- if/then/else
- for循环
- try/catch/finally
它也有一些唯一的结果,包括: - match表达式
- for表达式
在接下来的部分,我们会演示它们。
if/then/else
一个基本的Scala if语句看起来如下:
if (a == b) doSomething()
你也可以这样写:
if (a == b) {
doSomething()
}
if/else控制语句如下:
if (a == b) {
doSomething()
} else {
doSomethingElse()
}
复杂的 Scala if/else-if/else语句看起来如下:
if (test1) {
doX()
} else if (test2) {
doY()
} else {
doZ()
}
if表达式总是返回一个结果
关于Scala 中 if结构的很棒的事情就是它总是返回一个结果。你可以忽略这个结果,就像上面写的那样,但是一个更通常的方式 ---- 尤其是函数式编程中,会把这个结果分配给一个变量:
val minValue = if (a < b) a else b
有很多原因让人觉得很酷,其中一个事实是,Scala不再需要一个特别的三元运算符。
面向表达式编程
对编程作一个总的简短说明,当你写的每个表达式都返回一个值时,这种风格是指面向表达式编程,下面是一个表达式的例子:
val minValue = if (a < b) a else b
相反,那些不返回值的代码行叫做语句,为了它们的副作用而使用它们。
例如,下面的代码行不返回值,是为了它们的副作用而使用它们:
if (a == b) doSomething()
println("Hello")
上面的第一行代码 当a等于b时产生的副作用时运行doSomething方法。第二行代码是用它的副作用写一个字符串到STDOUT。
随着你更深入的学习Scala,你会发现自己写更多的表达式更少的语句。表达式和语句之间的不同也会变得更加明显。
for 循环
在最简单的使用中,for循环可以用来迭代集合中的元素。
例如,给定一个整数序列:
val nums = Seq(1,2,3)
你可以像下面这样遍历它,并输出它的值:
for (n <- nums) println(n)
下面是在REPL中的运行结果:
scala> val nums = Seq(1,2,3)
nums: Seq[Int] = List(1, 2, 3)
scala> for (n <- nums) println(n)
1
2
3
上面的例子使用了一个整数序列,它的类型是Sep[Int]; 下面有一个字符串的列表,它的数据类型是List[String] :
val people = List(
"Bill",
"Candy",
"Karen",
"Leo",
"Regina"
)
你依然可以就像前面那样使用一个for循环来打印它的值:
for (p <- people) println(p)
List 和 Seq 是两种类型的线性集合。在Scala中这些集合类比 Array 更受欢迎。(在后面会有更多介绍)
foreach 方法
为了遍历集合中的元素并打印它的内容,你还可以使用 foreach方法,在Scala 集合类中可以获取该方法。
例如,下面给出了如何用foreach方法来完成上面的那个示例:
people.foreach(println)
foreach方法在大多数集合类中都可以获取,包括 序列,映射,集。
在映射中使用for 和 foreach
当你在Scala中使用Map(与Java中的HashMap类似)时,你也可以使用 for 和 foreach。
例如,给定一个 电影名字 和 分级 的 Map :
val ratings = Map(
"Lady in the Water" -> 3.0,
"Snakes on a Plane" -> 4.0,
"You, Me and Dupree" -> 3.5
)
你可以像下面这样使用for来打印电影名字和分级:
for ((name,rating) <- ratings)
println(s"Movie: $name, Rating: $rating")
下面是在REPL中的演示结果:
scala> for ((name,rating) <- ratings) println(s"Movie: $name, Rating: $rating")
Movie: Lady in the Water, Rating: 3.0
Movie: Snakes on a Plane, Rating: 4.0
Movie: You, Me and Dupree, Rating: 3.5
在这个例子中,name对应于映射中的每个主键,rating对应分配给每个主键的分级值。
你也可以使用foreach来打印:
ratings.foreach {
case(movie, rating) => println(s"key: $movie, value: $rating")
}
for 表达式
如果你记得我们上面写的面向表达式编程和表达式与语句的不同,你会注意到在前面我们使用for关键字和foreach方法作为产生副作用的工具。我们使用它们通过println打印集合中的值到STDOUT。Java有相似的关键字,但很多编程者多年来也一直使用它们,却从未对它们如何改进给予过多关注。
一旦你开始使用Scala进行工作,你会看到在函数式编程语言中,你可以使用更强大的for表达式来替代for循环。在Scala中,for 表达式是 for 控制结构的一个不同的用法。
使用for循环 是为了它的副作用,而使用for表达式是为了从已经存在的集合中创建一个新的集合。
例如,给定一个整数序列:
val nums = Seq(1,2,3)
你可以创建一个新的序列,里面的值都翻倍:
val doubledNums = for (n <- nums) yield n * 2
这个表达式可以理解为:对整数序列nums中的每个数值n,把这个数值翻倍,再把所有新产生的数值分配给变量doubleNums。在REPL中结果如下:
scala> val doubledNums = for (n <- nums) yield n * 2
doubledNums: Seq[Int] = List(2, 4, 6)
正如REPL输出显示的,产生的新的列表包含下面的这些值:
List(2,4,6)
总的来说,for表达式的结果就是创建了一个新的名为 doubleNums 的变量,它的值通过原先存在的 nums 列表中每个值翻倍来得到。
把列表中的字符串改为首字母大写
你可以对字符串列表使用同样的方式来处理。
例如,给定一列小写的字符串:
val names = List("adam", "david", "frank")
你可以使用for表达式来创建一个首字母大写的字符串列表:
val ucNames = for (name <- names) yield name.capitalize
在REPL中展示如下:
scala> val ucNames = for (name <- names) yield name.capitalize
ucNames: List[String] = List(Adam, David, Frank)
成功啦!每个字符串的首字母都变成大写了。
yield 关键字
上面的两个 for 表达式都使用了 yield关键字:
val doubledNums = for (n <- nums) yield n * 2
-----
val ucNames = for (name <- names) yield name.capitalize
-----
在for后面接yield是“秘密酱汁”,就是“我想要使用所示的算法,从在for表达式中遍历的现有集合产生一个新的集合”。
在 yield 后使用一段代码
有时,也必须yield表达式后的代码来解决当前问题。
例如,给定如下的一列字符串:
val names = List("_adam", "_david", "_frank")
想象你要创建一个新的列表,里面的人名都需要首字母大写。为此,你首先需要移除每个名字开头的下划线,接着把首字母大写。为了移除下划线,你对每个String 调用 drop(1) ,然后调用 capitalize 方法。
下面是你如何用for表达式来解决这个问题:
val capNames = for (name <- names) yield {
val nameWithoutUnderscore = name.drop(1)
val capName = nameWithoutUnderscore.capitalize
capName
}
如果在REPL里运行,你会看到:
capNames: List[String] = List(Adam, David, Frank)
这个问题的一个更简短的解决方法
在上面我们展示了一个冗长的解决方法,这样你可以看到如何在yield 后编写多行代码。然而,对于这个例子,你可以用更Scala的方式来像下面这样写:
val capNames = for (name <- names) yield name.drop(1).capitalize
如果你喜欢,也可以加上大括号:
val capNames = for (name <- names) yield { name.drop(1).capitalize }
match 表达式
scala 有一个 match 表达式的概念。在大多数简单的情况下,你可以使用像Java的switch语句那样使用一个match表达式:
// i is an integer
i match {
case 1 => println("January")
case 2 => println("February")
case 3 => println("March")
case 4 => println("April")
case 5 => println("May")
case 6 => println("June")
case 7 => println("July")
case 8 => println("August")
case 9 => println("September")
case 10 => println("October")
case 11 => println("November")
case 12 => println("December")
// catch the default with a variable so you can print it
case _ => println("Invalid month")
}
match表达式是很棒的,因为它也可以返回值,这样在上个例子中,你可以把字符串分配给一个变量:
val monthName = i match {
case 1 => "January"
case 2 => "February"
case 3 => "March"
case 4 => "April"
case 5 => "May"
case 6 => "June"
case 7 => "July"
case 8 => "August"
case 9 => "September"
case 10 => "October"
case 11 => "November"
case 12 => "December"
case _ => "Invalid month"
}
用match表达式像上面这样产生一个值是一种常见的用法。
快速了解scala方法
scala让 match表达式 作为方法体也很简单。
我们还没有展示如何写方法,所有作为一个简短的介绍,下面有个 convertBooleanToStringMessage 方法,接收 Boolean 值,返回 String 值:
def convertBooleanToStringMessage(bool: Boolean): String = {
if (bool) "true" else "false"
}
下面展示当给 true 和 false 时,这个方法如何运行:
scala> val answer = convertBooleanToStringMessage(true)
answer: String = true
scala> val answer = convertBooleanToStringMessage(false)
answer: String = false
使用 match表达式 作为方法体
下面的方法使用match表达式作为方法体:
def convertBooleanToStringMessage(bool: Boolean): String = bool match {
case true => "you said true"
case false => "you said false"
}
因为这列出来所有可能的 Boolean 值,所有就不用写默认case语句了。
用match表达式做方法体也是一种常见的用法。
在一个case语句中处理多种情况
想象你需要处理:当为0或空的字符串返回false,其他任何值都返回true:
def isTrue(a: Any) = a match {
case 0 | "" => false
case _ => true
}
scala> isTrue(0)
res0: Boolean = false
scala> isTrue("")
res1: Boolean = false
scala> isTrue(1.1F)
res2: Boolean = true
scala> isTrue(new java.io.File("/etc/passwd"))
res3: Boolean = true
还有一些例子:
val evenOrOdd = i match {
case 1 | 3 | 5 | 7 | 9 => println("odd")
case 2 | 4 | 6 | 8 | 10 => println("even")
case _ => println("some other number")
}
cmd match {
case "start" | "go" => println("starting")
case "stop" | "quit" | "exit" => println("stopping")
case _ => println("doing nothing")
}
在case语句中使用if表达式
count match {
case 1 => println("one, a lonely number")
case x if x == 2 || x == 3 => println("two's company, three's a crowd")
case x if x > 3 => println("4+, that's a party")
case _ => println("i'm guessing your number is zero or less")
}
scala不需要在if表达式中用圆括号,如果你觉得可读性更好,也可以使用:
count match {
case 1 => println("one, a lonely number")
case x if (x == 2 || x == 3) => println("two's company, three's a crowd")
case x if (x > 3) => println("4+, that's a party")
case _ => println("i'm guessing your number is zero or less")
}
你也可以在 => 的右侧写多行代码:
count match {
case 1 =>
println("one, a lonely number")
case x if x == 2 || x == 3 =>
println("two's company, three's a crowd")
case x if x > 3 =>
println("4+, that's a party")
case _ =>
println("i'm guessing your number is zero or less")
}
或者加花括号:
count match {
case 1 => {
println("one, a lonely number")
}
case x if x == 2 || x == 3 => {
println("two's company, three's a crowd")
}
case x if x > 3 => {
println("4+, that's a party")
}
case _ => {
println("i'm guessing your number is zero or less")
}
}
还有一些例子:
i match {
case a if 0 to 9 contains a => println("0-9 range: " + a)
case b if 10 to 19 contains b => println("10-19 range: " + b)
case c if 20 to 29 contains c => println("20-29 range: " + c)
case _ => println("Hmmm...")
}
最后,展示如何在if表达式中引用类的字段:
stock match {
case x if (x.symbol == "XYZ" && x.price < 20) => buy(x)
case x if (x.symbol == "XYZ" && x.price > 50) => sell(x)
case x => doNothing(x)
}
try/catch/finally 表达式
一个 try/catch 例子,展示它的语法:
var text = ""
try {
text = openAndReadAFile(filename)
} catch {
case e: FileNotFoundException => println("Couldn't find that file.")
case e: IOException => println("Had an IOException trying to read that file")
}
当你需要最后做一些处理,例如关闭一些资源时:
try {
// your scala code here
}
catch {
case foo: FooException => handleFooException(foo)
case bar: BarException => handleBarException(bar)
case _: Throwable => println("Got some other kind of Throwable exception")
} finally {
// your scala code here, such as closing a database connection
// or file handle
}
这个语法的一大好处是,与 match 表达式的语法保持一致,这样会使你的代码更整洁更易读。