【Scala】Scala之Control Structures(二)

简介:  前面学习了Scala的Numbers,接着学习Scala的Control Structures(控制结构)。

 2.8 用一个case语句匹配多个条件

  

1. 问题描述

  当几个匹配条件要求执行相同的业务逻辑,你想要使用业务逻辑的一个副本来匹配条件,而不是为每个case重复业务逻辑

  

2. 解决方案

  将匹配条件放在一行使用|分割 

val i = 5
i match {
    case 1 | 3 | 5 | 7 | 9 => println("odd")
    case 2 | 4 | 6 | 8 | 10 => println("even")
}

 同理,该语法也适用于字符串或其他类型 

val cmd = "stop"
cmd match {
    case "start" | "go" => println("starting")
    case "stop" | "quit" | "exit" => println("stopping")
    case _ => println("doing nothing")
} 
trait Command
case object Start extends Command
case object Go extends Command
case object Stop extends Command
case object Whoa extends Command
def executeCommand(cmd: Command) = cmd match {
    case Start | Go => start()
    case Stop | Whoa => stop()
}


2.9. 将match表达式的值赋值给变量


1. 描述

你想要将匹配表达式返回值其赋值给变量,或使用匹配表达式作为方法的主体


2. 解决方案

  在表达式之前插入变量 

val evenOrOdd = someNumber match {
    case 1 | 3 | 5 | 7 | 9 => println("odd")
    case 2 | 4 | 6 | 8 | 10 => println("even")
}

这种方法经常被用作创建短的方法或函数 

def isTrue(a: Any) = a match {
    case 0 | "" => false
    case _ => true
}

 

2.10 访问匹配表达式默认情况下的值

  

1. 问题描述

你想要访问默认匹配值,但是使用通配符时无法访问到值

  

2. 解决方案

在默认情况下指定变量名而非_通配符

i match {
    case 0 => println("1")
    case 1 => println("2")
    case default => println("You gave me: " + default)
}

通过给默认匹配变量名称,你可以在语句右侧访问该变量

  3. 讨论

  在默认匹配时使用变量名而非通配符,若没有给出通配符时,可能抛出异常

  31.png

  2.11 在匹配表达式中使用模式匹配

  1. 问题描述

  你需要在匹配表达式中匹配一个或多个模式,该模式可以是常量模式、变量模式、构造函数模式、序列模式、元组模式或类型模式等

  2. 解决方案

  为要匹配的每个模式定义一个case语句 

def echoWhatYouGaveMe(x: Any): String = x match {
    // constant patterns
    case 0 => "zero"
    case true => "true"
    case "hello" => "you said 'hello'"
    case Nil => "an empty List"
    // sequence patterns
    case List(0, _, _) => "a three-element list with 0 as the first element"
    case List(1, _*) => "a list beginning with 1, having any number of elements"
    case Vector(1, _*) => "a vector starting with 1, having any number of elements"
    // tuples
    case (a, b) => s"got $a and $b"
    case (a, b, c) => s"got $a, $b, and $c"
    // constructor patterns
    case Person(first, "Alexander") => s"found an Alexander, first name = $first"
    case Dog("Suka") => "found a dog named Suka"
    // typed patterns
    case s: String => s"you gave me this string: $s"
    case i: Int => s"thanks for the int: $i"
    case f: Float => s"thanks for the float: $f"
    case a: Array[Int] => s"an array of int: ${a.mkString(",")}"
    case as: Array[String] => s"an array of strings: ${as.mkString(",")}"    
    case d: Dog => s"dog: ${d.name}"
    case list: List[_] => s"thanks for the List: $list"
    case m: Map[_, _] => m.toString
    // the default wildcard pattern
    case _ => "Unknown
}

上述方法可以匹配不同的类型和模式

  3. 讨论

  通常使用此技术时,你的方法希望传入的是基类或特性继承的实例,然后case语句引用该基类型的子类型

import java.io.File
sealed trait RandomThing
case class RandomFile(f: File) extends RandomThing
case class RandomString(s: String) extends RandomThing
class RandomNoiseMaker {
    def makeRandomNoise(t: RandomThing) = t match {
        case RandomFile(f) => playSoundFile(f)
        case RandomString(s) => speak(s)
    }
}    

 sealed表示密封的特质,其子类必须全部给出。

  有时你可能需要向模式添加变量,你可以用下面的一般语法:

  变量名 @ 模式

  这表示变量绑定模式,这种模式的含义是按照正常模式进行模式匹配,如果模式匹配成功,就像简单的变量模式一样,将变量设置为匹配对象。

  当匹配List类型时,我们可以如下 

case list: List[_] => s"thanks for the List: $list"

 当我们想要匹配以元素1作为头元素的列表时,我们可能会进行如下匹配  

case list: List(1, _*) => s"thanks for the List: $list"

 但此时会报错

32.png

解决办法是添加变量绑定模式  

case list @ List(1, _*) => s"$list"

2.12. 在匹配表达式中使用案例类

 1. 问题描述

 你希望在匹配表达式中匹配不同的case类(或case对象),如接收actor的消息时

 2. 解决方案

 根据你的需要可以使用之前的不同模式进行匹配

trait Animal
case class Dog(name: String) extends Animal
case class Cat(name: String) extends Animal
case object Woodpecker extends Animal
object CaseClassTest extends App {
    def determineType(x: Animal): String = x match {
        case Dog(moniker) => "Got a Dog, name = " + moniker
        case _:Cat => "Got a Cat (ignoring the name)"
        case Woodpecker => "That was a Woodpecker"
        case _ => "That was something else"
    }
    println(determineType(new Dog("Rocky")))
    println(determineType(new Cat("Rusty the Cat")))
    println(determineType(Woodpecker))
}

 2.13 将if表达式(守卫)添加到case语句中

  1. 问题描述

  你希望将匹配逻辑添加到匹配表达式中的case语句中,例如允许一系列数字或匹配模式,但仅当该模式与某些附加条件相匹配时

  2. 解决方案

  将if表达式添加至case语句中 

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...")
}

可以匹配不同的值

num match {
    case x if x == 1 => println("one, a lonely number")
    case x if (x == 2 || x == 3) => println(x)
    case _ => println("some other value")
}

 也可以在匹配时解析出字段信息 

def speak(p: Person) = p match {
    case Person(name) if name == "Fred" => println("Yubba dubba doo")
    case Person(name) if name == "Bam Bam" => println("Bam bam!")
    case _ => println("Watch the Flintstones!")
}

3. 讨论

  在进行匹配时也可将if放在=>的右侧  

case Person(name) =>
    if (name == "Fred") println("Yubba dubba doo")
    else if (name == "Bam Bam") println("Bam bam!")

 2. 14 使用匹配表达式而非isInstanceOf

  1. 问题描述

  你需要编写匹配一种类型或多个不同类型的代码块

  2. 解决方案

  你可以使用isInstanceOf来测试对象的类型  

if (x.isInstanceOf[Foo]) { do something ...}

然而并不鼓励这样做,更好的办法是使用匹配表达式  

def isPerson(x: Any): Boolean = x match {
    case p: Person => true
    case _ => false
}

或者给出一个继承已知父类的子类,需要根据子类的不同做出不同的操作  

trait SentientBeing
trait Animal extends SentientBeing
case class Dog(name: String) extends Animal
case class Person(name: String, age: Int) extends SentientBeing
def printInfo(x: SentientBeing) = x match {
    case Person(name, age) => // handle the Person
    case Dog(name) => // handle the Dog
}


  3. 讨论

  匹配表达式可以匹配多种类型,而不需要使用isInstanceOf方法

  2. 15. 在匹配表达式中使用列表

  1. 问题描述

  列表数据结构与其他集合数据结构有点不同,它从cons cells开始构建,并以Nil作为结尾,在使用匹配表达式时你想要利用此特性,例如编写递归程序

  2. 解决方案

  你可以使用如下方法创建List

  40.png

  当编写递归函数时,你可以利用其尾元素是Nil类型对象

  41.png

  3. 讨论

  在REPL中构建List时,不要忘记Nil情况

  2.16 使用try/catch匹配一个或多个异常

  1. 问题描述

  在try/catch块中你想要匹配一个或多个异常

  2. 解决方案

  Scala的try/catch/finally与Java类型,但是在catch中使用的是模式匹配 

val s = "Foo"
try {
    val i = s.toInt
} catch {
    case e: Exception => e.printStackTrace
}

当你需要匹配多个异常时,在catch添加即可 

try {
    openAndReadAFile(filename)
} catch {
    case e: FileNotFoundException => println("Couldn't find that file.")
    case e: IOException => println("Had an IOException trying to read that file")
}

3. 讨论

  Scala中的catch块可以匹配多个异常,如果你不关心发生的异常,可以使用如下方式 

try {
    openAndReadAFile("foo")
} catch {
    case t: Throwable => t.printStackTrace()
}
try {
    val i = s.toInt
} catch {
    case _: Throwable => println("exception ignored")
}

2.17 在try/catch/finally块之前声明变量

  1. 问题描述

  当你需要调用对象的闭包方法,你想要在try块中使用一个对象,并且在finally块中访问它

  2. 解决方案

  在try之前将字段声明为Option类型,之后在try块中创建一个Some对象 

import java.io._
object CopyBytes extends App {
    var in = None: Option[FileInputStream]
    var out = None: Option[FileOutputStream]
    try {
        in = Some(new FileInputStream("/tmp/Test.class"))
        out = Some(new FileOutputStream("/tmp/Test.class.copy"))
        var c = 0
        while ({c = in.get.read; c != −1}) {
            out.get.write(c)
        }
    } catch {
        case e: IOException => e.printStackTrace
    } finally {
        println("entered finally ...")
        if (in.isDefined) in.get.close
        if (out.isDefined) out.get.close
    }
}       

 2.18 自定义控制结构

  1. 问题描述

  你想要自定义控制结构来改善Scala语言

  2. 解决方案

  假如有一天你不喜欢while结构,而是如下结构 

package foo
import com.alvinalexander.controls.Whilst._
object WhilstDemo extends App {
    var i = 0
    whilst (i < 5) {
        println(i)
        i += 1
    }
}

 此时,whilist就是你自定义的控制结构,你可能采用如下方法定义whilist方法 

def whilst(testCondition: => Boolean)(codeBlock: => Unit) {
    while (testCondition) {
        codeBlock
    }
}

 但是更好的方法是不调用while循环  


package com.leesf.controls
import scala.annotation.tailrec
object Whilst {
    // 2nd attempt
    @tailrec
    def whilst(testCondition: => Boolean)(codeBlock: => Unit) {
        if (testCondition) {
            codeBlock
            whilst(testCondition)(codeBlock)
        }
    }
}

3. 讨论

  在第二版的whilist中,使用了递归调用,假设有一个两个参数的控制结构,当都为真时执行代码块,调用代码如下

doubleif(age > 18)(numAccidents == 0) { println("Discount!") }

 doubleif方法则可定义如下  

def doubleif(test1: => Boolean)(test2: => Boolean)(codeBlock: => Unit) {
    if (test1 && test2) {
        codeBlock
    }
}

其中,codeBlock: => Unit对应的是println("Discount!")


三、总结


  本篇博文学习了Scala的控制结构及其细节,同时还学习了自定义控制结构,也谢谢各位园友的观看~

 


目录
相关文章
|
算法 Java 编译器
【Scala】Scala之Control Structures(一)
 前面学习了Scala的Numbers,接着学习Scala的Control Structures(控制结构)。
257 0
【Scala】Scala之Control Structures(一)
|
2月前
|
分布式计算 大数据 Java
大数据-87 Spark 集群 案例学习 Spark Scala 案例 手写计算圆周率、计算共同好友
大数据-87 Spark 集群 案例学习 Spark Scala 案例 手写计算圆周率、计算共同好友
59 5
|
2月前
|
分布式计算 关系型数据库 MySQL
大数据-88 Spark 集群 案例学习 Spark Scala 案例 SuperWordCount 计算结果数据写入MySQL
大数据-88 Spark 集群 案例学习 Spark Scala 案例 SuperWordCount 计算结果数据写入MySQL
51 3
|
2月前
|
消息中间件 分布式计算 NoSQL
大数据-104 Spark Streaming Kafka Offset Scala实现Redis管理Offset并更新
大数据-104 Spark Streaming Kafka Offset Scala实现Redis管理Offset并更新
44 0
|
2月前
|
消息中间件 存储 分布式计算
大数据-103 Spark Streaming Kafka Offset管理详解 Scala自定义Offset
大数据-103 Spark Streaming Kafka Offset管理详解 Scala自定义Offset
98 0
|
2月前
|
分布式计算 大数据 Java
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
31 1
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
|
2月前
|
SQL 分布式计算 Java
大数据-96 Spark 集群 SparkSQL Scala编写SQL操作SparkSQL的数据源:JSON、CSV、JDBC、Hive
大数据-96 Spark 集群 SparkSQL Scala编写SQL操作SparkSQL的数据源:JSON、CSV、JDBC、Hive
47 0
|
2月前
|
缓存 分布式计算 大数据
大数据-90 Spark 集群 RDD 编程-高阶 RDD容错机制、RDD的分区、自定义分区器(Scala编写)、RDD创建方式(一)
大数据-90 Spark 集群 RDD 编程-高阶 RDD容错机制、RDD的分区、自定义分区器(Scala编写)、RDD创建方式(一)
55 0
|
2月前
|
分布式计算 算法 大数据
大数据-90 Spark 集群 RDD 编程-高阶 RDD容错机制、RDD的分区、自定义分区器(Scala编写)、RDD创建方式(二)
大数据-90 Spark 集群 RDD 编程-高阶 RDD容错机制、RDD的分区、自定义分区器(Scala编写)、RDD创建方式(二)
56 0
|
6月前
|
分布式计算 资源调度 Java
Scala+Spark+Hadoop+IDEA实现WordCount单词计数,上传并执行任务(简单实例-下)
Scala+Spark+Hadoop+IDEA实现WordCount单词计数,上传并执行任务(简单实例-下)
67 0