• Scala 高阶(九):Scala中的模式匹配


    大家好,我是百思不得小赵。

    创作时间:2022 年 7 月 21 日
    博客主页: 🔍点此进入博客主页
    —— 新时代的农民工 🙊
    —— 换一种思维逻辑去看待这个世界 👀
    今天是加入CSDN的第1236天。觉得有帮助麻烦👏点赞、🍀评论、❤️收藏



    本次主要分享Scala中关于模式匹配的内容,Scala中的模式匹配类似于Java中的switch语法,但是Scala在基于Java的思想上补充了特有的功能。

    一、概述

    基本语法

    value match {
        case caseVal1 => returnVal1
        case caseVal2 => returnVal2
        ...
        case _ => defaultVal
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    举个例子:

      val x: Int = 2
        val y: String = x match {
          case 1 => "one"
          case 2 => "two"
          case 3 => "three"
          case _ => "other"
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    第二个例子:

        // 示例:使用模式匹配实现简单的二元运算
        val a = 25
        val b = 21
    
        def matchOualop(op: Char) = op match {
          case '+' => a + b
          case '-' => a - b
          case '*' => a * b
          case '/' => a / b
          case _ => "非法运算"
        }
    
        println(matchOualop('+'))
        println(matchOualop('-'))
        println(matchOualop('\\'))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 如果所有 case 都不匹配,那么会执行 case _ 分支,类似于 Java 中 default 语句,若此时没有 case _ 分支,那么会抛出 MatchError
    • 每个 case 中,不需要使用 break 语句,自动中断 case
    • match case 语句可以匹配任何类型,而不只是字面量。
    • => 后面的代码块,直到下一个 case 语句之前的代码是作为一个整体执行,可以使用{}括起来,也可以不括。

    二、模式守卫

    需要进行匹配某个范围的数据内容的时候,可以在模式匹配中进行模式守卫的操作,类似于for推倒式中的循环守卫。

    举个例子:

    object Test01_PatternMatchBase {
      def main(args: Array[String]): Unit = {
        // 模式守卫
        // 求一个整数的绝对值
        def abs(num: Int) = num match {
          case i if i >= 0 => i
          case i if i < 0 => -i
        }
    
        println(abs(11))
        println(abs(-11))
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    三、模式匹配类型

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

    常量

        // 匹配常量
        def describeConst(s: Any): String = s match {
          case 1 => "number 1"
          case "hello" => "String hello"
          case '+' => "+"
          case 's' => "Char s"
          case _ => ""
        }
        
        println(describeConst(1))
        println(describeConst('s'))
        println(describeConst(11))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    类型

    	// 匹配类型
    	 def deprecateType(x: Any): String = x match {
    	      case i: Int => "int" + i
    	      case s: String => "String" + s
    	      case list: List[String] => "list" + list
    	      case array: Array[Int] => "Array[Int]" + array.mkString("-")
    	      case a => "Something type"
    	    }
    
        println(deprecateType(23))
        println(deprecateType("12"))
        println(deprecateType(List("mi", "hao")))
        println(deprecateType(List(12, 21)))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    数组

    对于数组可以定义多种匹配形式,可以定义模糊的元素类型匹配、元素数量匹配或者精确的某个数组元素值匹配

    	// 匹配数组
        for (arr <- List(
          Array(0),
          Array(1, 0),
          Array(0, 1, 0),
          Array(1, 1, 0),
          Array(12, 21, 34, 32),
          Array("hello", 34, 32),
        )) {
          val result = arr match {
            case Array(0) => "0"
            case Array(1, 0) => "Array(1,0)"
            case Array(x, y) => "Array:" + x + ", " + y
            case Array(0, _*) => "以0开头的数组"
            case Array(x, 1, y) => "中间为1 的三元数组"
            case _ => "something else"
          }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    列表

    使用::运算符匹配first :: second :: rest,将一个列表拆成三份,第一个第二个元素和剩余元素构成的列表。

        // 匹配列表 方式一
        for (list <- List(
          List(0),
          List(1, 0),
          List(0, 0, 0),
          List(1, 1, 0),
          List(999)
        )) {
          val result = list match {
            case List(0) => "0"
            case List(x, y) => "list:" + x + ", " + y
            case List(0, _*) => "以0开头的list"
            case List(a) => "list(a)" + a
            case _ => "something else"
          }
        }
    
        // 方式二
        val list = List(1, 34, 3, 2, 3, 2, 3, 6)
        list match {
          case first :: second :: rest => println(s"first:$first second: $second rest: $rest")
          case _ => "something else"
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    元组

    可以匹配n元组、匹配元素类型、匹配元素值。如果只关心某个元素,其他就可以用通配符或变量。元组大小固定,所以不能用_*

        // 元组类型
        for (tuple <- List(
          (0, 1),
          (0, 1),
          (0, 1, 0),
          (0, 1, 1),
          ("hello", true, 0.5)
        )) {
          val result = tuple match {
            case (a,b) => " "+ a + ", " +b
            case (0,_) => "(0,_)"
            case (a,1,_) => "(a,1,_)" + a
            case (x,y,z) => "(x,y,z)"
            case _ => "something else"
          }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    对象及样例类

    匹配对象

    object Test04_MathObject {
      def main(args: Array[String]): Unit = {
        val student = new Student("alice", 15)
    
        //针对对象的实例进行匹配
        val result = student match {
          case Student("alice", 15) => "alice, 15"
          case _ => "else"
        }
      }
    }
    
    class Student(val name: String, val age: Int)
    
    object Student {
      def apply(name: String, age: Int): Student = new Student(name, age)
    
      // 实现unapply
      def unapply(student: Student): Option[(String, Int)] = {
        if (student == null){
          None
        } else{
          Some(student.name,student.age)
        }
      }
    }
    
    • 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
    • 当将 Student("alice", 15))写在 case 后时 case Student("alice", 15) => "alice, 20",会默认调用 unapply 方法(对象提取器),student 作为 unapply
      方法的参数,unapply 方法将 student 对象的 nameage 属性提取出来,与
      Student("alice", 15)) 中的属性值进行匹配
    • case 中对象的 unapply 方法(提取器)返回 Some,且所有属性均一致,才算匹配成功,
      属性不一致,或返回 None,则匹配失败。
    • 若只提取对象的一个属性,则提取器为 unapply(obj:Obj):Option[T]
    • 若提取对象的多个属性,则提取器为 unapply(obj:Obj):Option[(T1,T2,T3…)]
    • 若提取对象的可变个属性,则提取器为 unapplySeq(obj:Obj):Option[Seq[T]]

    样例类

    case class 类名 (参数1,参数2......)
    
    • 1
    • 样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中自动提供了一些常用的方法,如 apply、unapply、toString、equals、hashCodecopy
    • 样例类是为模式匹配而优化的类,因为其默认提供了 unapply 方法,因此,样例类可以直接使用模式匹配,而无需自己实现 unapply 方法。
    • 构造器中的每一个参数都成为 val修饰的变量
    object Test_MatchCaseClass {
      def main(args: Array[String]): Unit = {
        val student = Student("alice", 15)
    
        val result = student1 match {
          case Student("alice", 15) => "alice, 20"
          case _ => "else"
        }
        println(result)
      }
    }
    
    case class Student(name: String, age: Int)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    四、声明变量中的模式匹配

    变量声明也可以是一个模式匹配的过程。

    object Test_MathTupleExtend {
      def main(args: Array[String]): Unit = {
        // 1.在变量声明时匹配
        val (x,y) = (10,"hello")
        println(s"$x $y")
        val List(first,second,_*) = List(12,21,21,21,34)
        println(s"$first $second ")
    
        val fir :: sec :: res =List(12,21,34)
        println(s"$fir $sec $res")
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    五、for表达式模式匹配

    • 元组中取元素时,必须用_1 _2 …,可以用元组赋值将元素赋给变量
    • 指定特定元素的值,可以实现类似于循环守卫的功能
    object TestMatchFor {
    	 def main(args: Array[String]): Unit = {
    		 val map = Map("A" -> 1, "B" -> 0, "C" -> 3)
    		 // //直接将 map 中的 k-v 遍历出来
    		 for ((k, v) <- map) { 
    		 	println(k + " -> " + v) //3 个
    		 }
    
    		 //遍历 value=0 的 k-v ,如果 v 不是 0,过滤
    		 for ((k, 0) <- map) {
    			 println(k + " --> " + 0) // B->0
    		 }
    
    		 //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
    • 19

    六、偏函数模式匹配

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

    偏函数定义

    val second: PartialFunction[List[Int], Option[Int]] = {
     case x :: y :: _ => Some(y)
    }
    
    • 1
    • 2
    • 3
    • second:偏函数名称
    • PartialFunction[List[Int], Option[Int]]: 偏函数类型
    • 该偏函数的功能是返回输入的 List 集合的第二个元素

    举个例子:

    object Test_PartialFunction {
      def main(args: Array[String]): Unit = {
        val list = List(("a,", 12), ("b", 34), ("c", 45))
    
        // map转换 key不变 value两倍
        val newList = list.map(tuple => (tuple._1, tuple._2 * 2))
    
        // 模式匹配 对元素元素赋值
        val newList2 = list.map(
          tuple => {
            tuple match {
              case (word, count) => (word, count * 2)
            }
          }
        )
    
        // 省略lambda表达式 表示偏函数
        val newList3 = list.map {
          case (word, count) => (word, count * 2)
        }
    
        // 函数应用 求绝对值
        val positiveAbs: PartialFunction[Int, Int] = {
          case x if x >= 0 => x
        }
    
        val pnegativeAbs: PartialFunction[Int, Int] = {
          case x if x < 0 => -x
        }
    
        def abs(x: Int): Int= (positiveAbs orElse pnegativeAbs)(x)
    
        println(abs(21))
      }
    }
    
    • 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

    本次Scala中的模式匹配部分到这里就结束了,知识点较为简单但是使用起来特别的灵活,希望对大家有所帮助!!!

    在这里插入图片描述

  • 相关阅读:
    【Proteus仿真】L297驱动步进电机
    【Leetcode】 17. 电话号码的字母组合
    如何将48位立即数加载到ARM通用寄存器中?
    面试题五:computed的使用
    Mybatis-plus插件的一次完美实践
    猿创征文 | 盘点10个冷门Python库,原来Python还能实现这些功能?
    微服务框架 SpringCloud微服务架构 20 RestClient 操作索引库 20.4 创建索引库
    LeetCode 第 388 场周赛个人题解
    PMI-ACP(103:1-16)
    算法笔记:点四叉树
  • 原文地址:https://blog.csdn.net/Zp_insist/article/details/125915842