尚硅谷Scala (12)

本文详细介绍了Scala中的模式匹配机制,包括基本的`match`和`case`用法,无条件匹配、类型匹配、守卫条件、变量绑定、元组匹配、数组匹配、列表匹配、对象匹配以及样例类的使用。通过示例展示了如何在不同场景下进行高效且灵活的模式匹配操作。
摘要由CSDN通过智能技术生成

十二、模式匹配

12.1 match

12.1.1 基本介绍

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

12.1.2 scala match 的快速入门案例

object MatchDemo01 {
  def main(args: Array[String]): Unit = {
    val oper = "#"
    val n1=20
    val n2=10
    var res=0

    //说明
    //1. match (类似 java switch) 和 case 是关键字
    //2. 如果匹配成功, 则 执行 => 后面的代码块. //3. 匹配的顺序是从上到下,匹配到一个就执行对应的 代码
    //4. => 后面的代码块 不要写 break ,会自动的退出 match
    //5. 如果一个都没有匹配到,则执行 case _ 后面的代码块
    oper match{
      case "+" =>{
        res=n1+n2
        println("ok")
        println("hello world")
      }
      case "-" =>res=n1-n2
      case "*" =>res=n1*n2
      case "/" =>res=n1/n2
      case _ => println("oper error")
  }
}

12.1.3 match 的细节和注意事项

1) 如果所有 case 都不匹配,那么会执行 case _ 分支,类似于 Java default 语句
2) 如果所有 case 都不匹配,又没有写 case _ 分支,那么会抛出 MatchError
3) 每个 case 中,不用 break 语句,自动中断 case
4) 可以在 match 中使用其它类型,而不仅仅是字符
5) => 等价于 java swtich :
6) => 后面的代码块到下一个 case , 是作为一个整体执行,可以使用 {} 扩起来,也可以不扩。

12.2 守卫

12.2.1 基本介绍

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

12.2.2 应用案例

object MatchIfDemo01 {
  def main(args: Array[String]): Unit = {
    for (ch <- "+-*/3!"){
      var sign=0
      var digit=0
      ch match{
        // 说明..
        // 如果 case 后有 条件守卫即 if ,那么这时的 _ 不是表示默认匹配
        // 表示忽略 传入 的 ch
        case '+' => sign= 1
        case '-' => sign= -1
        case _ if ch.toString.equals("3") => digit=3
        case _ if ch>10 =>println("ch>10")
        case _ => sign=2
      }
      println(ch+" "+sign+" "+digit)

//      + 1 0
//      - -1 0
//      ch>10
//      * 0 0
//      ch>10
//      / 0 0
//      3 0 3
//      ch>10
//      ! 0 0
    }
  }
}

12.3 模式中的变量

12.3.1 基本介绍

如果在 case 关键字后跟变量名,那么 match 前表达式的值会赋给那个变量

12.3.2 应用案例

object MatchVar {
  def main(args: Array[String]): Unit = {
    val ch= 'V'
    ch match{
      case '+' =>println("ok")
      // 下面 case mychar 含义是 mychar = ch
      case mychar =>println("ok~"+mychar) //ok~V
      case _ => println("ok~~")
    }

    val ch1 = '#'
    //match 是一个表达式,因此可以有返回值
    //返回值就是匹配到的代码块的最后一句话的值
    val res = ch1 match {
      case '#' => ch1 + " hello "
      case _ => println ("ok~~")
    }
    println("res= " + res) //res= # hello 
  }
}

12.4 类型匹配

12.4.1 基本介绍

可以匹配 对象的任意类型 ,这样做避免了使用 isInstanceOf asInstanceOf 方法

12.4.2 应用案例

object MatchTypeDemo01 {
  def main(args: Array[String]): Unit = {
    val a=5
    //说明 obj 实例的类型 根据 a 的值来返回
    val obj =if(a==1) 1
    else if (a == 2) "2"
    else if (a == 3) BigInt(3)
    else if (a == 4) Map("aa" -> 1)
    else if (a == 5) Map(1 -> "aa")
    else if (a == 6) Array(1, 2, 3)
    else if (a == 7) Array("aa", 1)
    else if (a == 8) Array("aa")


    //说明
    //1. 根据 obj 的类型来匹配
    // 返回值
    val result =obj match{
      //a:  相当于把obj赋值给a
      case a: Int=>a
      case b: Map[String, Int] => "对象是一个字符串-数字的 Map 集合"
      case c: Map[Int, String] => "对象是一个数字-字符串的 Map 集合"
      case d: Array[String] => d //"对象是一个字符串数组"
      case e: Array[Int] => "对象是一个数字数组"
      case f: BigInt => Int.MaxValue
      case _ => "啥也不是"
    }
    println(result)
  }
}

12.4.3 类型匹配注意事项

1) Map[String, Int] Map[Int, String] 是两种不同的类型,其它类推。
2) 在进行类型匹配时,编译器会预先检测是否有可能的匹配,如果没有则报错 .
3) 一个说明 :
val result = obj match {
        case i : Int => i
} case i : Int => i 表示 将 i = obj ( 其它类推 ) ,然后再判断类型
4) 如果 case _ 出现在 match 中间,则表示隐藏变量名,即不使用 , 而不是表示默认匹配

12.5 匹配数组

12.5.1 基本介绍

1) Array(0) 匹配只有一个元素且为 0 的数组。
2) Array(x,y) 匹配数组有两个元素,并将两个元素赋值为 x y 。当然可以依次类推 Array(x,y,z) 匹配数组有 3 个元素的等等 ....
3) Array(0,_*) 匹配数组以 0 开始

12.5.2 应用案例

object MatchArr {
  def main(args: Array[String]): Unit = {
    val arrs = Array(Array(0), Array(1, 0), Array(0, 1, 0),
      Array(1, 1, 0), Array(1, 1, 0, 1))
    for (arr <- arrs) {
      val result = arr match {
        case Array(0) => "0"
        case Array(x, y) => x + "和" + y
        case Array(0, _*) => "以0开头的数组"
        case _ => "什么集合都不是"
      }
      println("result= " + result)
      //      result= 0
      //      result= 1和0
      //      result= 以0开头的数组
      //      result= 什么集合都不是
      //      result= 什么集合都不是
    }

    println("===============================")
    //给你一个数组集合,如果该数组是 Array(10,20) , 请使用默认匹配,返回 Array(20,10)
    val arrs2 = Array(Array(0), Array(1, 0), Array(0, 1, 0),
      Array(1, 1, 0), Array(1, 1, 0, 1))
    for (arr2 <- arrs2) {
      val result2 = arr2 match {
        case Array(x, y) => Array(y, x)
        case _ => "不处理了"
      }
      println("数组的前后交换:" + result2)
    }
  }
}

12.6 匹配列表

应用案例
object MatchList {
  def main(args: Array[String]): Unit = {
    for(list <- Array(List(0),List(1,0),List(88),List(0,0,0),List(1,0,0))){
      val result =list match{
        case 0 :: Nil => "0" //
        case x :: y :: Nil => x + " " + y //
        case 0 :: tail => "0 ..." //
        case x :: Nil => x
        case _ => "something else"
      }
      println(result)
      //1. 0
      //2. 1 0
      //88
      //3. 0 ...
      // 4. something else
    }
  }
}

12.7 匹配元组
应用案例
object MatchTupleDemo01 {
  def main(args: Array[String]): Unit = {
    //如果要匹配 (10, 30) 这样任意两个元素的对偶元组,应该如何写
    for (pair <- Array((0, 1), (1, 0), (10, 30), (1, 1), (1, 0, 2))) {
      val result = pair match { //
        case (0, _) => "0 ..." //
        case (y, 0) => y //
        case (x, y) => (y, x) //"匹配到(x,y)" + x + " " + y
        case _ => "other" //.
       }
          //1. 0 ...
          //2. 1
          //3.(30,10)
          //(1,1)
          //4. other
          println(result)
      }
    }
  }

12.8 对象匹配

12.8.1 基本介绍

对象匹配,什么才算是匹配呢?,规则如下 :
1) case 中对象的 unapply 方法 ( 对象提取器 ) 返回 Some 集合则为匹配成功
2) 返回 None 集合则为匹配失败

12.8.2 快速入门案例

object MatchObject {
  def main(args: Array[String]): Unit = {
    // 模式匹配使用:
    val number:Double =36.0

    number match{
      //说明 case Square(n) 的运行的机制
      //1. 当匹配到 case Square(n)
      //2. 调用 Square 的 unapply(z: Double),z 的值就是 number
      //3. 如果对象提取器 unapply(z: Double) 返回的是 Some(6) ,则表示匹配成功,同时
      // 将 6 赋给 Square(n) 的 n
      //4. 如果对象提取器 unapply(z: Double) 返回的是 None ,则表示匹配不成功
      case Square(n) => println("匹配成功 n=" + n)
      case _ => println("nothing matched")
    }
  }
}

object Square{
  //说明
  //1. unapply 方法是对象提取器
  //2. 接收 z:Double 类型
  //3. 返回类型是 Option[Double]
  //4. 返回的值是 Some(math.sqrt(z)) 返回 z 的开平方的值,并放入到 Some(x)
  def unapply(z:Double):Option[Double] =Some(math.sqrt(z))
  def apply(z:Double):Double=z*z
}

12.8.3 应用案例 2

object MatchObjectDemo2 {
  def main(args: Array[String]): Unit = {
    val namesString = "Alice,Bob,Thomas" //字符串
    //说明
    namesString match {
      // 当 执行 case Names(first, second, third)
      // 1. 会调用 unapplySeq(str),把 "Alice,Bob,Thomas" 传入给 str
      // 2. 如果 返回的是 Some("Alice","Bob","Thomas"),分别给 (first, second, third)
      // 注意,这里的返回的值的个数需要和 (first, second, third)要一样
      // 3. 如果返回的 None ,表示匹配失败
      case Names(first, second, third) => {
        println("the string contains three people's names")
        // 打印字符串
        println(s"$first $second $third")
      }
      case _ => println("nothing matched")
    }
  }
}

//object
object Names {
  //当构造器是多个参数时,就会触发这个对象提取器
  def unapplySeq(str: String): Option[Seq[String]] = {
    //使用str.contains(",")判断字符串str是否包含逗号。如果包含逗号,则使用str.split(",")将字符串按逗号拆分成一个字符串数组
    if (str.contains(",")) Some(str.split(","))
    else None
  }
}
代码的小结
1) case 后面的对象提取器方法的参数为多个,则会默认调用 def unapplySeq() 方法
2) 如果 unapplySeq 返回是 Some ,获取其中的值 , 判断得到的 sequence 中的元素的个数是否是三个如果是三个,则把三个元素分别取出,赋值给 first second third
3) 其它的规则不变 .

12.9 变量声明中的模式

12.9.1 基本介绍

match 中每一个 case 都可以单独提取出来,意思是一样的 .

12.9.2 应用案例

object MatchVarDemo {
  def main(args: Array[String]): Unit = {
    val (x,y)=(1,2)
    val (q,r)=BigInt(10) /%3  //说明 q = BigInt(10) / 3 r = BigInt(10) % 3
    val arr=Array(1,7,2,9)
    val Array(first,second,_*)=arr  // 提出 arr 的前两个元素
    println(first,second)
  }
}

12.10 for 表达式中的模式

12.10.1 基本介绍

for 循环也可以进行模式匹配 .

12.10.2 应用案例

object MatchForDemo {
  def main(args: Array[String]): Unit = {
    val map = Map("A" -> 1, "B" -> 0, "C" -> 3)
    for((k,v) <- map){
      println(k+" -> " +v)
//      A -> 1
//      B -> 0
//      C -> 3
    }
    for((k,0) <- map){
      println(k+" -->"+0) //B -->0
    }

    for((k,v) <- map if v==0) {
      println(k+" --->" +v) //B --->0
    }
  }
}

12.11 样例(模板)

12.11.1 样例类快速入门

object CaseClassDemo01 {
  def main(args: Array[String]): Unit = {
    println("hello")
  }
}

abstract class Amount
case class Dollar(value:Double) extends Amount  //样例类
case class Currency(value:Double,unit:String) extends Amount  //样例类
case object NoAmount extends Amount //样例类
//类型(对象) =序列化(serializable)==>字符串(1.你可以保存到文件中【freeze】2.反序列化,2 网络传输)

12.11.2 基本介绍

1) 样例类仍然是类
2) 样例类用 case 关键字进行声明。
3) 样例类是为 模式匹配而优化 的类
4) 构造器中的每一个参数都成为 val ——除非它被显式地声明为 var (不建议这样做)
5) 在样例类对应的伴生对象中提供 apply 方法让你不用 new 关键字就能构造出相应的对象
6) 提供 unapply 方法让模式匹配可以工作
7) 将自动生成 toString equals hashCode copy 方法 ( 有点类似模板类,直接给生成,供程序员使用)
8) 除上述外,样例类和其他类完全一样。你可以添加方法和字段,扩展它们

12.11.3 样例类最佳实践 1

object CaseClassDemo02 {
  def main(args: Array[String]): Unit = {
    //该案例的作用就是体验使用样例类方式进行对象匹配简洁性
    for(amt <- Array(Dollar2(1000.0),Currency2(10000.0,"RMB"),NoAmount2)){
      val result = amt match{
        case Dollar2(v) => "$"+v
        case Currency2(v,u) => v +" "+u
        case NoAmount2 => ""
      }
      println(amt+": "+result)
//      Dollar2(1000.0): $1000.0
//      Currency2(10000.0,RMB): 10000.0 RMB
//      NoAmount2:
    }
  }
}

abstract class Amount2
case class Dollar2(value:Double) extends Amount2  //样例类
case class Currency2(value:Double,unit:String) extends Amount2  //样例类
case object NoAmount2 extends Amount2 //样例类

12.11.4 样例类最佳实践 2

说明
样例类的 copy 方法和带名参数
copy 创建一个与现有对象值相同的新对象,并可以通过带名参数来修改某些属性。
代码实现
object CaseClassDemo03 {
  def main(args: Array[String]): Unit = {
    val amt = new Currency3(3000.0, "RMB")
    val amt2 = amt.copy()  //克隆,创建的对象和 amt 的属性一样
    println("amt2.value="+amt2.value+" amt2.unit="+amt2.unit)  //amt2.value=3000.0 amt2.unit=RMB
    println(amt2) //Currency3(3000.0,RMB)

    val amt3 = amt.copy(value = 8000)
    println(amt3) //Currency3(8000.0,RMB)

    val amt4 = amt.copy(unit = "美元")
    println(amt4) //Currency3(3000.0,美元)
  }
}

abstract class Amount3
case class Dollar3(value:Double) extends Amount3  //样例类
case class Currency3(value:Double,unit:String) extends Amount3  //样例类
case object NoAmount3 extends Amount3 //样例类

12.12 case 语句的中置()表达式

12.12.1 基本介绍

什么是中置表达式? 1 + 2 ,这就是一个中置表达式。如果 unapply 方法产出一个元组,你可以在
case 语句中使用中置表示法。比如可以匹配一个 List 序列

12.12.2 应用实例

object MidCase {
  def main(args: Array[String]): Unit = {
    List(1,10,80,4,5) match{
      case first::second::rest =>println(first+" "+second+" "+rest.length+" "+rest)
      case _ => println("匹配不到...")

      //1 10 3 List(80, 4, 5)
    }
  }
}

12.13匹配嵌套结构

12.13.1 基本介绍

操作原理类似于正则表达式

12.13.2 最佳实践案例-商品捆绑打折出售

现在有一些商品,请使用 Scala 设计相关的样例类,完成商品捆绑打折出售。要求
1) 商品捆绑可以是单个商品,也可以是多个商品。
2) 打折时按照折扣 x 元进行设计 .
3) 能够统计出所有捆绑商品打折后的最终价格
object ScalesDemo0 {
  def main(args: Array[String]): Unit = {
    //这里给出了一个具体的打折的案例
    //(40-10)+(80+30-20)=120
    val sale = Bundle("书籍", 10, Book("漫画", 40), Bundle("文学作品", 20, Book("《阳关", 80), Book(" 《围城》", 30)))

    //知识点一:使用case语句,得到"漫画"
    val res = sale match {
      case Bundle(_, _, Book(desc, _), _*) => desc
    }
    println("res=" + res) //漫画

    //知识点 2-通过@表示法将嵌套的值绑定到变量。_*绑定剩余 Item 到 rest
    val res2 =sale match {
      case Bundle(_,_,art @Book(_,_),rest @_*) => (art,rest)
    }
    println("res2="+res2) //res2=(Book(漫画,40.0),ArraySeq(Bundle(文学作品,20.0,ArraySeq(Book(《阳关,80.0), Book( 《围城》,30.0)))))

    //知识点 3-不使用_*绑定剩余 Item 到 rest
    val res3=sale match {
      case Bundle(_,_ ,art3 @Book(_,_),rest3) =>(art3,rest3)
    }
    println("res3="+res3) //res3=(Book(漫画,40.0),Bundle(文学作品,20.0,ArraySeq(Book(《阳关,80.0), Book( 《围城》,30.0))))
  }
}

//设计样例类
abstract class Item

case class Book(description: String, price: Double) extends Item

//Bundle 捆 , discount: Double 折扣 , item: Item* ,
case class Bundle(Description: String, discount: Double, item: Item*) extends Item

12.13.3 最佳实践案例-商品捆绑打折出售(最终解决方案)

object ScalesDemo0 {
  def main(args: Array[String]): Unit = {
    //这里给出了一个具体的打折的案例
    //(40-10)+(80+30-20)=120
    val sale = Bundle("书籍", 10, Book("漫画", 40), Bundle("文学作品", 20, Book("《阳关", 80), Book(" 《围城》", 30)))

    //知识点一:使用case语句,得到"漫画"
    val res = sale match {
      case Bundle(_, _, Book(desc, _), _*) => desc
    }
    println("res=" + res) //漫画

    //知识点 2-通过@表示法将嵌套的值绑定到变量。_*绑定剩余 Item 到 rest
    val res2 =sale match {
      case Bundle(_,_,art @Book(_,_),rest @_*) => (art,rest)
    }
    println("res2="+res2) //res2=(Book(漫画,40.0),ArraySeq(Bundle(文学作品,20.0,ArraySeq(Book(《阳关,80.0), Book( 《围城》,30.0)))))

    //知识点 3-不使用_*绑定剩余 Item 到 rest
    val res3=sale match {
      case Bundle(_,_ ,art3 @Book(_,_),rest3) =>(art3,rest3)
    }
    println("res3="+res3) //res3=(Book(漫画,40.0),Bundle(文学作品,20.0,ArraySeq(Book(《阳关,80.0), Book( 《围城》,30.0))))

    println(price(sale))  //120.0

  }
  //解决方案
  def price(it:Item):Double={
    it match {
      case Book(_,p) =>p
      case Bundle(_,disc,its@_*) =>its.map(price).sum-disc
    }
  }
}

//设计样例类
abstract class Item

case class Book(description: String, price: Double) extends Item

//Bundle 捆 , discount: Double 折扣 , item: Item* ,
case class Bundle(Description: String, discount: Double, item: Item*) extends Item
解决原理:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值