控制结构

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类似)时,你也可以使用 forforeach
例如,给定一个 电影名字 和 分级 的 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 表达式的语法保持一致,这样会使你的代码更整洁更易读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值