Scala(第八章 模式匹配)

Scala中模式匹配类似于Java中的switch语法

int i = 10
switch (i) {
 case 10 :
   System.out.println("10");
    break;
 case 20 : 
    System.out.println("20");
    break;
 default : 
   System.out.println("other number");
   break;
}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

但是scala从语法中补充了更多的功能,所以更加强大

8.1 基本语法

模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有case都不匹配,那么会执行case_分支,类似于Java中default语句。

object TestMatchCase {
 def main(args: Array[String]): Unit = {
	 var a: Int = 10
	 var b: Int = 20
	 var operator: Char = 'd'
	 var result = operator match {
		 case '+' => a + b
		 case '-' => a - b
		 case '*' => a * b
		 case '/' => a / b
		 case _ => "illegal"
	 }
     println(result)
 }
}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

1)说明

1、如果所有case都不匹配,那么会执行case_分支,类似于Java中default语句,若此时没有case_分支,那么会抛出MatchError。
2、每个case中,不需要使用break语句,自动中断case。
3、match case语句可以匹配任何类型,而不只是字面量
4、=> 后面的代码块,直到下一个case语句之前的代码是作为一个整体执行,可以使用{}括起来,也可以不括。

8.2 模式守卫

1)说明

如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。

2)案例实操

object TestMatchGuard {
 def main(args: Array[String]): Unit = {
	 def abs(x: Int) = x match {
	 case i: Int if i >= 0 => i
	 case j: Int if j < 0 => -j
	 case _ => "type illegal"
	 }
	 println(abs(-5))
 }
}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

8.3 模式匹配类型

8.3.1 匹配常量

1)说明

Scala中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等。

2)实操

object TestMatchVal {
 def main(args: Array[String]): Unit = {
  println(describe(6))
 }
 def describe(x: Any) = x match {
	 case 5 => "Int five"
	 case "hello" => "String hello"
	 case true => "Boolean true"
	 case '+' => "Char +"
 }
}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

8.3.2 匹配类型

1)说明

需要进行类型判断时,可以使用前文所学的isInstanceOf[T]和asInstanceOf[T],也可使用模式匹配实现同样的功能。

2)案例实操

object TestMatchClass {
 def describe(x: Any) = x match {
 case i: Int => "Int"
 case s: String => "String hello"
 case m: List[_] => "List"
 case c: Array[Int] => "Array[Int]"
 case someThing => "something else " + someThing
 }
 def main(args: Array[String]): Unit = {
 //泛型擦除
	 println(describe(List(1, 2, 3, 4, 5)))
	 //数组例外,可保留泛型
	 println(describe(Array(1, 2, 3, 4, 5, 6)))
	 println(describe(Array("abc")))
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

8.3.3 匹配数组

1)说明

scala模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的,且第一个元素为0的数组。

2)案例实操

object TestMatchArray {
 def main(args: Array[String]): Unit = {
 for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), 
Array(1, 1, 0), Array(1, 1, 0, 1), Array("hello", 90))) { // 对一个数组集合进行遍历
 val result = arr match {
 case Array(0) => "0" //匹配 Array(0) 这个数组
 case Array(x, y) => x + "," + y //匹配有两个元素的数组,然后将将元素值赋给对应的 x,y
 case Array(0, _*) => "以 0 开头的数组" //匹配以 0 开头和数组
 case _ => "something else"
 }
 println("result = " + result)
 }
 }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

8.3.4 匹配列表

1)方式一

object TestMatchList {
 def main(args: Array[String]): Unit = {
 //list 是一个存放 List 集合的数组
 //请思考,如果要匹配 List(88) 这样的只含有一个元素的列表,并原值返回.应该怎么写
 for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 
0, 0), List(88))) {
 val result = list match {
 case List(0) => "0" //匹配 List(0)
 case List(x, y) => x + "," + y //匹配有两个元素的 List
 case List(0, _*) => "0 ..."
 case _ => "something else"
 }
 println(result)
 }
 }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2)方式二

object TestMatchList {
 def main(args: Array[String]): Unit = {
 val list: List[Int] = List(1, 2, 5, 6, 7)
 list match {
 case first :: second :: rest => println(first + "-" + 
second + "-" + rest)
 case _ => println("something else")
 }
 }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

8.3.5 匹配元组

object TestMatchTuple {
 def main(args: Array[String]): Unit = {
 //对一个元组集合进行遍历
 for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2))) {
 val result = tuple match {
 case (0, _) => "0 ..." //是第一个元素是 0 的元组
 case (y, 0) => "" + y + "0" // 匹配后一个元素是 0 的对偶元组
 case (a, b) => "" + a + " " + b
 case _ => "something else" //默认
 }
 println(result)
 }
 }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

扩展案例

object TestGeneric {
def main(args: Array[String]): Unit = {
 //特殊的模式匹配 1 打印元组第一个元素
 for (elem <- Array(("a", 1), ("b", 2), ("c", 3))) {
 println(elem._1)
 }
 for ((word,count) <- Array(("a", 1), ("b", 2), ("c", 3))) {
 println(word)
 }
 for ((word,_) <- Array(("a", 1), ("b", 2), ("c", 3))) {
 println(word)
 }
 for (("a",count) <- Array(("a", 1), ("b", 2), ("c", 3))) {
 println(count)
 }
 println("--------------")
 //特殊的模式匹配 2 给元组元素命名
 var (id,name,age): (Int, String, Int) = (100, "zs", 20)
 println((id,name,age))
 println("--------------")
 //特殊的模式匹配 3 遍历集合中的元组,给 count * 2
 var list: List[(String, Int)] = List(("a", 1), ("b", 2), ("c", 3))
 //println(list.map(t => (t._1, t._2 * 2)))
 println(
 list.map{
 case (word,count)=>(word,count*2)
 }
 )
 var list1 = List(("a", ("a", 1)), ("b", ("b", 2)), ("c", ("c", 3)))
 println(
 list1.map{
 case (groupkey,(word,count))=>(word,count*2)
 }
 )
 }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

8.3.6 匹配对象及样例类

1)基本语法

class User(val name: String, val age: Int)
object User{
 def apply(name: String, age: Int): User = new User(name, age)
 def unapply(user: User): Option[(String, Int)] = {
 if (user == null)
 None
 else
 Some(user.name, user.age)
 }
}
object TestMatchUnapply {
 def main(args: Array[String]): Unit = {
 val user: User = User("zhangsan", 11)
 val result = user match {
 case User("zhangsan", 11) => "yes"
 case _ => "no"
 }
 println(result)
 }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

小结:
1、val user = User(“zhangsan”,11),该语句在执行时,实际调用的是 User 伴生对象中的apply 方法,因此不用 new 关键字就能构造出相应的对象。
2、当将 User(“zhangsan”, 11)写在 case 后时[case User(“zhangsan”, 11) => “yes”],会默
认调用 unapply 方法(对象提取器),user 作为 unapply 方法的参数,unapply 方法将 user 对象的 name 和 age 属性提取出来,与 User(“zhangsan”, 11)中的属性值进行匹配
3、case 中对象的 unapply 方法(提取器)返回 Some,且所有属性均一致,才算匹配成功,属性不一致,或返回 None,则匹配失败。
4、若只提取对象的一个属性,则提取器为 unapply(obj:Obj):Option[T]
若提取对象的多个属性,则提取器为 unapply(obj:Obj):Option[(T1,T2,T3…)]
若提取对象的可变个属性,则提取器为 unapplySeq(obj:Obj):Option[Seq[T]】

2)样例类

1、语法:
case class Person (name: String, age: Int)
2、说明:
2.1:样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中自动提供了一些常用的方法,如 apply、unapply、toString、equals、hashCode 和 copy。
2.2:样例类是为模式匹配而优化的类,因为其默认提供了 unapply 方法,因此,样例类可以直接使用模式匹配,而无需自己实现 unapply 方法。
2.3:构造器中的每一个参数都成为 val,除非它被显式地声明为 var(不建议这样做)
3、实操
上述匹配对象的案例使用样例会节省大量代码
在这里插入图片描述

8.4 变量声明中的模式匹配

case class Person(name: String, age: Int)
object TestMatchVariable {
 def main(args: Array[String]): Unit = {
 val (x, y) = (1, 2)
 println(s"x=$x,y=$y")
 val Array(first, second, _*) = Array(1, 7, 2, 9)
 println(s"first=$first,second=$second")
 val Person(name, age) = Person1("zhangsan", 16)
 println(s"name=$name,age=$age")
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

8.5 for表达式中的模式匹配

object TestMatchFor {
 def main(args: Array[String]): Unit = {
 val map = Map("A" -> 1, "B" -> 0, "C" -> 3)
 for ((k, v) <- map) { //直接将 map 中的 k-v 遍历出来
 println(k + " -> " + v) //3 个
 }
 println("----------------------")
 //遍历 value=0 的 k-v ,如果 v 不是 0,过滤
 for ((k, 0) <- map) {
 println(k + " --> " + 0) // B->0
 }
 println("----------------------")
 //if v == 0 是一个过滤的条件
 for ((k, v) <- map if v >= 1) {
 println(k + " ---> " + v) // A->1 和 c->33
 }
 }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

8.6 偏函数中的模式匹配(了解)

偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查。例如该偏函数的输入类型为 List[Int],而我们需要的是第一个元素是 0 的集合,这就是通过模式匹配实现的。

1)偏函数定义

val second: PartialFunction[List[Int], Option[Int]] = {
 case x :: y :: _ => Some(y)
}
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

注:该偏函数的功能是返回输入的List集合的第二个元素

2)偏函数原理

上述代码会被 scala 编译器翻译成以下代码,与普通函数相比,只是多了一个用于参数检查的函数——isDefinedAt,其返回值类型为Boolean。

val second = new PartialFunction[List[Int], Option[Int]] {
 //检查输入参数是否合格
 override def isDefinedAt(list: List[Int]): Boolean = list match 
{
 case x :: y :: _ => true
 case _ => false
 }
 //执行函数逻辑
 override def apply(list: List[Int]): Option[Int] = list match 
{
 case x :: y :: _ => Some(y)
 }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3)偏函数使用

1、偏函数不能像 second(List(1,2,3))这样直接使用,因为这样会直接调用 apply 方法,而应该调用 applyOrElse 方法,如下
second.applyOrElse(List(1,2,3), (_: List[Int]) => None)
2、applyOrElse 方法的逻辑为 if (ifDefinedAt(list)) apply(list) else default。如果输入参数满足条件,即 isDefinedAt 返回 true,则执行 apply 方法,否则执行 defalut 方法,default 方法为参数不满足要求的处理逻辑。

4)案例实操
4.1 需求

将该 List(1,2,3,4,5,6,“test”)中的 Int 类型的元素加一,并去掉字符串。

def main(args: Array[String]): Unit = {
 val list = List(1,2,3,4,5,6,"test")
 val list1 = list.map {
 a =>
 a match {
 case i: Int => i + 1
 case s: String =>s + 1
 }
 }
 println(list1.filter(a=>a.isInstanceOf[Int]))
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

4.2 实操

方法一:
List(1,2,3,4,5,6,"test").filter(_.isInstanceOf[Int]).map(_.asInstanceOf[Int] + 1).foreach(println)
方法二:
List(1, 2, 3, 4, 5, 6, "test").collect { case x: Int => x + 1 }.foreach(println)

 
 
  • 1
  • 2
  • 3
  • 4

下一章(第九章) 异常

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值