scala-basic 基础篇
@author 鲁伟林
Scala基础知识介绍,主要包括《Scala编程实战》的基础章节
GitHub地址: https://github.com/thinkingfioa/scala-in-action
本人博客地址: http://blog.csdn.net/thinking_fioa/article/details/78265745
3. 控制结构
Scala语言和Java语言在控制结构部分很像,但是又存在有趣的差别。 第3章项目源码阅读
3.1 for和foreach循环
循环语句主要用于处理三个问题: 1. 遍历一个集合中的所有元素; 2. 对集合中的每个元素进行某个操作; 3. 利用现有的集合创建新的集合
3.1.1 for循环常用的遍历
个人比较推荐如下for循环语法遍历集合
def forF(array : Array[String]): Unit = {
for(entry <- array) {
println(entry.toUpperCase())
}
}
3.1.2 从for循环中返回值
使用for/ield组合,输入一个集合,返回一个新的集合。在for循环添加yield实际上将每次循环的结果放入一个临时存放区。等循环结束后,以集合的形式返回。
def forYield(array : Array[String]) : Array[String] = {
for(entry <- array) yield {
entry.toUpperCase()
}
}
3.1.3 for循环计数器
访问循环内的计数器,即通过一个计数器访问数组元素,使用array.indices
def forCount(array : Array[String]): Unit = {
for(i <- array.indices) {
println(s"$i is ${array(i)}")
}
}
3.1.4 遍历一个Map
遍历Map中的键值对,下面的Map循环方法最简洁和可读
def forMap(namesMap : Map[String, Int]) : Unit = {
for((k,v) <- namesMap) {
println(s"key is $k, value is $v")
}
}
3.1.5 另一种遍历集合方式
使用foreach, map, flatmap, collect, reduce等方法
- array.foreach(println) ----- 遍历array数组,可以使用自定义方法替换println方法
- array.foreach( e => println(e.toUpperCase)) ----- 使用匿名函数的语法
- array.foreach{ e => | val s = e.toUpperCase | println(s)|} ----- 实现多行函数
3.1.6 for循环式是如何被解释的
提供简化版的规则,帮助理解for循环执行过程
- 遍历集合的一个简单的for循环被解释为foreach方法调用
- 带有卫语句的for循环(3.3节)被解释为一个foreach调用后在集合上调用withFilter方法的序列
- 带有yield表达式的for循环被解释为集合上调用map方法
- 带有yield表达式和卫语句被解释为在集合上调用withFilter方法,紧接着一个map方法
3.2 在for循环中使用多个计数器
创建有多个计数器的循环,如遍历多维数组的情况。推荐使用大括号的代码风格,如下:
代码
def forMoreCount() : Unit = {
for {
i <- 1 to 3
j <- 1 to 5
k <- 1 to 10
} {
println("next: ")
println(s"i : $i, j : $j, k : $k")
}
}
3.3 在for循环中嵌入if语句(卫语句)
for循环中添加一个或者多个条件语句,典型的应用场景就是将一个元素从集合中过滤掉。 推荐使用大括号编码风格
代码
def forIf() : Unit = {
for {
i <- 1 to 10
if i> 3
if i<= 8
if i % 2 == 0
} println(i)
}
3.4 创建for表达式( for/yield组合 )
对一个已有的集合中的每个元素应用某个算法,从而生成新的集合。在for循环中使用yield语句的方式通常被叫作for推导
代码
def forYieldMoreLine(array : Array[String]) : Array[Int] = {
for( e <- array) yield {
val eUpper = e.toUpperCase()
eUpper.length
}
}
理解for/yield表达式
- 开始运行时,for/yield循环立刻创建一个新的空集合(Bucket),类型与输入的集合相同
- for循环每次遍历,都会在输入集合中的每个元素基础上创建新的元素,加入到Bucket中
- 循环结束后,Bucket中的所有内容都被返回
3.5 实现break和continue
遗憾的是Scala没有break或者continue关键字,但是scala.util.control.Breaks提供了类似的功能。相对于Java来说,Scala的break和continue较为复杂化。个人不喜欢这种风格
代码
import util.control.Breaks._
object Ctrl3P5 {
def forBreak(array : Array[Int]) : Unit = {
breakable {
for(i <- array) {
if(i== 2) {
break()
} else {
print(i+", ")
}
}
}
}
def forContinue(array : Array[Int]) : Unit = {
for(i <- array) {
breakable {
if(i== 2) {
break()
} else {
print(i +", ")
}
}
}
}
}
3.6 像三元运算符一样使用if
Java提供条件运算符 ? : 称为三元运算符,但是Scala没有提供。在scala中只能使用if/else语句。eg x >=0 ? "yes": "no" 等价于 if(x>=0) "yes" else "no"
3.7 像swtich语句一样使用匹配表达式
Java中基于整数的switch语句,Scala也支持使用@switch注解来满足switch语句,同时@switch注解性能会更优越
代码
def switchTypo(i : Int) : String = {
(i : @switch) match {
case 1 => "One"
case 2 => "Two"
case _ => "Other"
}
}
匹配表达式不局限于整数,它是非常灵活的,如下类型的匹配也是支持的
代码
def switchType(x : Any) : String = {
(x : @switch) match {
case s : String => "One"
case i : Int => "Two"
case _ => "Other"
}
}
3.7.1 处理缺省情况
处理缺省情况的两种情况:
- 不关心缺省匹配的值,使用通配符去捕获 ----- case _ => println("default")
- 关系缺省匹配的值,指定一个变量,然后在表达式右侧使用该变量 ----- case default => println(default)
- 如果不处理缺省情况,可能会产生MatchError错误。所以建议要处理default case
3.8 一条case语句匹配多个条件
多个匹配条件需要执行相同的业务逻辑时,使用一条case语句匹配多个条件
代码
def moreCase(x : Int) : Unit = {
(x : @switch) match {
case 1 | 3 | 5 | 7 | 9 => println("odd")
case 2 | 4 | 6 | 8 | 10 => println("even")
}
}
3.9 将匹配表达式的结果赋值给变量
将一个匹配表达式返回值赋值给一个变量,或将匹配表达式作为方法的主体
def isTrue(a : Any) = a match{
case 0 | "" => false
case _ => true
}
3.12 在匹配表达式中使用Case类
在一个匹配表达式中匹配不同的case类(或者case对象)。如下面的代码,Dog和Cat case类以及Woodpecker case对象都是Animal trait的子类型
代码
trait Animal
case class Dog(name : String) extends Animal
case class Cat(name : String) extends Animal
case object Woodpecker extends Animal
object Ctrl3P11 {
def main(args: Array[String]): Unit = {
println(determineType(Dog("ppp")))
println(determineType(Cat("fioa")))
println(determineType(Woodpecker))
}
def determineType(x : Animal) : String = x match {
case Dog(moniker) => "Got a Dog, name = "+moniker
case _ : Cat => "Got a Cat"
case Woodpecker => "That was a Wood"
case _ => "default"
}
}
3.13 给Case语句添加if表达式(卫语句)
给匹配的表达式内的case语句添加合适的逻辑,帮助增强case语句的约束条件
object Ctrl3P13 {
def main(args: Array[String]): Unit = {
caseIfNum(1)
caseIfNum(2)
}
def caseIfNum(x : Int) : Unit = {
(x : @switch) match {
case m if m==1 => println("one, a lonely number")
case n if n==2 || n==3 => println(n)
case _ => println("default")
}
}
}
3.14 使用匹配表达式替换isInstanceOf
判断对象是否匹配一个类型,可以通过使用isInstanceOf来判断,eg: if(x isInstanceOf[Class]) 。但当需求负责情况写,写一个代码块去匹配一种类型或者多个不同的类型的可读性更好。
代码
trait SentientBeing
trait Animal2 extends SentientBeing
case class Pig(name : String) extends Animal2
case class Person(name:String, age : Int) extends SentientBeing
object Ctrl3P14 {
def main(args: Array[String]): Unit = {
casePrintInfo(Person("luweilin", 24))
casePrintInfo(Pig("ppp"))
if(Pig("ppp").isInstanceOf[SentientBeing]) {
println("true")
}
}
def casePrintInfo(x : SentientBeing) : Unit = x match {
case Person(name, age) => println(s"$name is $name, age is $age")
case Pig(name) => println(s"pig name is $name")
case _ => println("default")
}
}
3.15 在匹配的表达式中使用List
List数据结构和其他的集合数据结构略有不同。列表由单元开始,Nil元素结尾。如果下递归打印内容
object Ctrl3P15 {
def main(args: Array[String]): Unit = {
val x = List(1,2,3,4)
println(caseList(x))
}
def caseList(x : List[Int]) : String = x match {
case s :: rest => s +", " + caseList(rest)
case Nil => ""
}
}
3.16 用try/catch匹配一个或多个异常
在try/catch块捕捉一个或者更多的异常
def moreException(fileName : String) : Unit = {
try {
// read config File
} catch {
case e : FileNotFoundException => println("Colud find that file.")
case e : IOException => println("IOException trying to read that file")
}
}
注意: Scala中没有受检异常,因此不需要指定抛出异常的方法。如果需要声明方法抛出的异常,或者需要和Java交互,在定义方法时添加@throws注解
@throws(classOf[NumberFormatException])
def throwException(fileName : String) : Unit = {
try {
// read config File
} catch {
case e : NumberFormatException => throw e
}
}
3.17 在try/catch/finally块中使用变量前定义变量问题
在try代码中使用一个变量,并在finally代码块中访问该对象,如调用对象的close方法。一般情况下,在try/catch块前声明字段为Option,然后在try子句中创建一个Some对象,finally中执行关闭。在Scala中建议不要使用null
代码:
def tryCatchFinally(fileName : String) : Unit = {
var in = None : Option[FileInputStream]
try {
// open file Name
} catch {
case cause: IOException => cause.printStackTrace()
} finally {
if(in.isDefined) {
in.get.close()
}
}
}
3.18 创建自定义控制结构
Scala语言的创造者有意决定通过Scala类库去实现功能而不是去创建一些关键字。典型的例子就是: break和continue关键字。开发者可以自定义控制结构,创建可用的DSL去给他人所用
3.18.1 创建一个类似于while循环控制结构
object Ctrl3P18 {
def main(args: Array[String]): Unit = {
var i =0
whilst(i<5) {
println(s"index: $i")
i += 1
}
}
@tailrec
def whilst(testCondition : => Boolean) (codeBlock : => Unit) {
if(testCondition) {
codeBlock
whilst(testCondition)(codeBlock)
}
}
}
解释:
自定义函数名:whilst,接受两个参数列表,第一个是参数列表测试条件,一个表示用户想要运行的代码块