Scala的模式匹配

Scala的模式匹配

Scala 中的模式匹配类似于Java 中的 switch 语法:下面是java中switch代码:

java 复制代码
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;
}

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

基本语法

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

scala 复制代码
object Test_PatternMatchBase {
  def main(args: Array[String]): Unit = {
    // 1. 基本定义语法
    val x: Int = 5
    val y: String = x match {
      case 1 => "one"
      case 2 => "two"
      case 3 => "three"
      case _ => "other"
    }
    println(y)

    // 2. 示例:用模式匹配实现简单二元运算
    val a = 25
    val b = 13

    def matchDualOp(op: Char): Int = op match {
      case '+' => a + b
      case '-' => a - b
      case '*' => a * b
      case '/' => a / b
      case '%' => a % b
      case _ => -1
    }

    println(matchDualOp('+'))
    println(matchDualOp('/'))
    println(matchDualOp('\\'))

    println("=========================")

    // 3. 模式守卫
    // 求一个整数的绝对值
    def abs(num: Int): Int = {
      num match {
        case i if i >= 0 => i
        case i if i < 0 => -i
      }
    }

    println(abs(67))
    println(abs(0))
    println(abs(-24))
  }
}
  • 说明
    (1) 如果所有 case 都不匹配,那么会执行 case _ 分支,类似于 Java 中 default 语句,若此时没有case _ 分支,那么会抛出MatchError。
    (2) 每个case 中,不需要使用break 语句,自动中断case。
    (3) match case 语句可以匹配任何类型,而不只是字面量。
    (4) => 后面的代码块,直到下一个 case 语句之前的代码是作为一个整体执行,可以使用{}括起来,也可以不括。
    在 Scala 中,模式匹配(Pattern matching)是一种强大的特性,用于对数据进行分析和处理。它可以在一个表达式中确定一个值是否匹配了某个模式,并且在匹配成功时执行相应的代码块。模式匹配可以应用于各种类型的对象,包括整数、字符、字符串、列表等。

模式匹配基本使用示例

Scala 的模式匹配语法类似于 switch 语句,但更加强大和灵活。以下是一些基本的模式匹配示例:

scala 复制代码
val x: Any = 123

x match {
  case i: Int => println("整数:" + i)
  case s: String => println("字符串:" + s)
  case d: Double => println("双精度浮点数:" + d)
  case _ => println("未知类型")
}

在上面的代码中,我们将一个任意类型的值 x 进行了模式匹配。首先,我们使用 case i: Int => 匹配整数类型并将其赋值给变量 i,然后执行 println("整数:" + i) 输出整数。接着,我们使用 case s: String => 匹配字符串类型,并输出对应的信息。最后,我们使用 _ 表示其他所有类型并输出 "未知类型"。

除了基本类型之外,模式匹配还支持对列表、元组等复杂类型进行匹配,例如:

scala 复制代码
def sum(lst: List[Int]) : Int = lst match {
  case Nil => 0
  case x :: tail => x + sum(tail)
}

println(sum(List(1, 2, 3))) // 输出 6

在上面的代码中,我们定义了一个 sum 函数用于计算列表中所有整数的和。在函数内部,我们使用模式匹配来处理两种情况:当列表为空时(case Nil => 0),返回 0;否则,我们使用 case x :: tail => 匹配列表的头部元素 x 和剩余元素 tail,并递归地调用 sum(tail) 来计算剩余元素的和,并将其与头部元素 x 相加。

需要注意的是,在模式匹配中,每个匹配分支都必须满足类型一致性和互斥性,以避免出现匹配二义性的情况。

模式守卫

  1. 说明
    如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。
  2. 代码示例
scala 复制代码
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))
}
}

模式匹配类型

在 Scala 中,模式匹配非常灵活,可以在各种情况下使用。以下是一些常见的模式匹配运用情况示例:

匹配基本类型和常量值

scala 复制代码
val x: Any = 123

x match {
  case 0 => println("匹配到 0")
  case 1 | 2 => println("匹配到 1 或 2")
  case i: Int if i > 100 => println("匹配到大于 100 的整数")
  case _ => println("未匹配到")
}

匹配元组

scala 复制代码
val tuple: (String, Int) = ("Scala", 3)

tuple match {
  case ("Java", _) => println("第一个元素是 Java")
  case (str, num) if num > 0 => println(s"第一个元素是 $str,第二个元素是正整数")
  case _ => println("未匹配到")
}

匹配列表List

scala 复制代码
val list: List[Int] = List(1, 2, 3)

list match {
  case Nil => println("空列表")
  case head :: tail => println(s"头部元素是 $head,剩余元素是 $tail")
}

匹配类和对象

匹配对象的例子

scala 复制代码
/**
 * 对象与对象的属性进行模式匹配:
 *   1.需要定义一个类和伴生对象,在伴生对象中需要定义创建对象的方法apply方法
 *   2. 由于对象直接的比较是地址引用的比较,如果需要使用模式匹配对对象的属性值进行判断,
 *   就需要在伴生对象中定义unapply方法用于结构对象实例。unapply方法用来对对象属性进行拆解
 */
object Test_MatchObject {
  def main(args: Array[String]): Unit = {
    val student = new Student("alice", 19)

    // 针对对象实例的内容进行匹配
    val result = student match {
      case Student("alice", 18) => "Alice, 18"
      case _ => "Else"
    }

    println(result)
  }
}

// 定义类
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))
    }
  }
}

示例二

scala 复制代码
sealed trait Shape
case class Circle(radius: Double) extends Shape
case class Rectangle(width: Double, height: Double) extends Shape
case object UnknownShape extends Shape

val shape: Shape = Circle(3.0)

shape match {
  case Circle(r) => println(s"圆形,半径是 $r")
  case Rectangle(w, h) => println(s"矩形,宽度是 $w,高度是 $h")
  case UnknownShape => println("未知形状")
}

使用模式守卫进行更复杂的匹配

scala 复制代码
val x: Any = List(1, 2, 3)

x match {
  case list: List[_] if list.nonEmpty => println("非空列表")
  case list: List[_] if list.isEmpty => println("空列表")
  case str: String if str.length > 10 => println("字符串长度大于 10")
  case _ => println("未匹配到")
}

匹配数组

在 Scala 中,匹配数组与匹配列表的方式类似。你可以使用模式匹配对数组进行处理。以下是一个示例:

scala 复制代码
val array: Array[Int] = Array(1, 2, 3)

array match {
  case Array() => println("空数组")
  case Array(1) => println("单个元素数组")
  case Array(1, 2, _*) => println("以1和2开头的数组")
  case Array(_, _, 3) => println("以3结尾的数组")
  case _ => println("未匹配到")
}

在上面的代码中,我们匹配了不同类型的数组情况。首先,使用 case Array() 匹配空数组;然后,使用 case Array(1) 匹配只包含一个元素的数组;接着,使用 case Array(1, 2, _*) 匹配以 1 和 2 开头的任意长度的数组;最后,使用 case Array(_, _, 3) 匹配以 3 结尾的数组。如果没有匹配到任何情况,就会执行最后一个 case 分支。

需要注意的是,在匹配可变长度的数组时,我们使用了 _* 表示任意长度的数组片段。

变量声明中的模式匹配

在 Scala 中,可以在变量声明中使用模式匹配来提取和赋值变量的值。这种方式被称为模式匹配的变量声明或解构赋值。以下是一个示例:

scala 复制代码
val tuple: (String, Int) = ("Scala", 3)

val (str, num) = tuple

println(s"字符串:$str")
println(s"整数:$num")

在上面的代码中,我们声明了一个元组 tuple,包含两个元素:一个字符串和一个整数。然后,我们使用模式匹配的方式将元组中的值解构并赋给了两个新的变量 strnum。这样,我们就可以通过这两个变量来访问元组中的值。

变量声明时模式匹配案例

scala 复制代码
object Test03_MatchTupleExtend {
  def main(args: Array[String]): Unit = {
    // 1. 在变量声明时匹配
    val (x, y) = (10, "hello")
    println(s"x: $x, y: $y")

    //变量声明是匹配
    val (a,b) = (100,"hello")
    println(s"a: $a, b: $b")

    val List(first, second, _*) = List(23, 15, 9, 78)
    println(s"first: $first, second: $second")
    // 使用双冒号模式匹配的方式进行赋值
    val fir :: sec :: rest = List(23, 15 , 9, 78)
    println(s"first: $fir, second: $sec, rest: $rest")

    println("=====================")

    // 2. for推导式中进行模式匹配,List[ (String, Int) ],集合list里面是多个二元组
    val list: List[(String, Int)] = List(("a", 12), ("b", 35), ("c", 27), ("a", 13))

    // 2.1 原本的遍历方式
    for (elem <- list){
      println(elem._1 + " " + elem._2)
    }

    // 2.2 将List的元素直接定义为元组,对变量赋值
    for ((word, count) <- list ){
      println(word + ": " + count)
    }

    println("-----------------------")
    // 2.3 可以不考虑某个位置的变量,只遍历key或者value,比如只遍历,打印元组的第一个元素word,不要count
    for ((word, _) <- list)
      println(word)

    println("-----------------------")

    // 2.4 可以指定某个位置的值必须是多少
    // 指定第一个元素为a的value值打印出来
    for (("a", count) <- list){
      println(count)
    }
  }
}

除了元组,还可以在变量声明中匹配其他数据结构,比如列表、数组等。示例如下:

scala 复制代码
val list: List[String] = List("Hello", "World")

val head :: tail = list

println(s"头部元素:$head")
println(s"剩余元素:$tail")

在上面的代码中,我们声明了一个列表 list,包含两个字符串元素。然后,使用模式匹配的变量声明方式将列表的头部元素赋给变量 head,剩余元素赋给变量 tail。这样,我们就可以分别访问列表的头部元素和剩余元素。

需要注意的是,模式匹配的变量声明在匹配失败时会抛出 MatchError 异常。为了避免这种情况,请确保变量声明与待解构的数据结构具有相同的结构和类型。

匹配对象及样例类

样例类

在Scala中,样例类(Case Class)是一种特殊的类,用于定义不可变的数据模型。样例类,自动默认创建伴生对象,并生成apply方法和解析对象的unapply方法,还实现了equalshashCodetoString等方法。

它具有许多有用的功能,使得数据建模和处理更加简洁和方便。

以下是样例类的特点和使用方式:

  1. 自动创建类的伴生对象:定义样例类时,编译器会自动生成一个与类同名的伴生对象,其中包含了一些常用的方法和属性。

  2. 不可变性:样例类的实例默认是不可变的,即无法在创建后修改其状态。

  3. 默认实现了equalshashCodetoString等方法:编译器会自动生成这些方法的实现,使得比较、哈希和输出对象更加方便。

  4. 模式匹配的支持:样例类通常用于模式匹配,可以方便地匹配和提取类的属性。

下面是一个简单的示例来说明如何定义和使用样例类:

scala 复制代码
case class Person(name: String, age: Int)

val alice = Person("Alice", 25)
val bob = Person("Bob", 30)

println(alice.name) // 输出: Alice

val olderPerson = alice.copy(age = alice.age + 1)
println(olderPerson) // 输出: Person(Alice, 26)

alice match {
  case Person("Alice", age) => println(s"Alice is $age years old.")
  case Person(name, age) => println(s"$name is $age years old.")
}

上述示例中,定义了一个名为Person的样例类。它有两个属性:nameage。使用样例类的主构造函数可以直接创建对象,而不需要使用new关键字。

可以通过点号.访问对象的属性,如alice.name。还可以使用copy方法创建一个新的对象,同时修改部分属性的值。

match表达式中,可以使用模式匹配来处理不同的Person对象。这使得根据对象的属性进行处理变得更加简单和直观。

总之,样例类提供了一种方便和强大的方式来定义不可变的数据对象,并且自动提供了一些常用方法和模式匹配的支持。它们在函数式编程和数据建模方面非常有用。


样例类使用模式匹配示例代码

scala 复制代码
/** 样例类:
 * 在Scala中,样例类(Case Class)是一种特殊的类,用于定义不可变的数据模型。
 * 样例类,自动默认创建伴生对象,并生成apply方法和解析对象的unapply方法,
 *还实现了equals、hashCode、toString等方法。
 */
object Test05_MatchCaseClass {
  def main(args: Array[String]): Unit = {
    val student = Student1("alice", 18)

    // 针对对象实例的内容进行匹配
    val result = student match {
      case Student1("alice", 18) => "Alice, 18"
      case _ => "Else"
    }

    println(result)
  }
}
// 定义样例类
case class Student1(name: String, age: Int)

在 Scala 中,可以使用模式匹配来匹配各种类型的对象,包括普通类和样例类。其中,样例类是一种特殊的类,它专门用于模式匹配,因为编译器会自动生成带有模式匹配相关方法的代码。以下是一个示例:

scala 复制代码
sealed trait Fruit
case class Apple(color: String) extends Fruit
case class Banana(length: Int) extends Fruit
case object UnknownFruit extends Fruit

val fruit: Fruit = Apple("red")

fruit match {
  case Apple("red") => println("红色苹果")
  case Banana(len) if len > 10 => println("长度大于 10 的香蕉")
  case UnknownFruit => println("未知水果")
  case _ => println("其他水果")
}

在上面的代码中,我们定义了一个 Fruit 样例类及两个子类 AppleBanana,还有一个对象 UnknownFruit。然后,我们创建了一个 Apple 对象并将其赋给 fruit 变量。接着,使用 match 关键字来匹配不同类型的 Fruit 对象,分别输出相应的提示信息。

需要注意的是,当匹配样例类时,可以使用模式匹配提取出样例类中的属性值,并对其进行比较。在上面的代码中,使用 case Apple("red") 匹配颜色为红色的苹果。

嵌套模式匹配

当数据结构较为复杂时,可以使用嵌套的模式匹配来逐级解构和匹配。

scala 复制代码
sealed trait Location
case class City(name: String) extends Location
case class Country(name: String, capital: City) extends Location

def printCapital(location: Location): Unit = {
  location match {
    case Country(_, City(capital)) => println(s"首都是$capital")
    case _ => println("未知位置")
  }
}

在上面的代码中,我们定义了一个 Location 类及其两个子类 City 和 Country。Country 类包含一个 City 类的实例作为首都。通过嵌套的模式匹配,我们可以提取和打印出国家的首都。

相关推荐
大G哥6 分钟前
java提高正则处理效率
java·开发语言
VBA633716 分钟前
VBA技术资料MF243:利用第三方软件复制PDF数据到EXCEL
开发语言
轩辰~18 分钟前
网络协议入门
linux·服务器·开发语言·网络·arm开发·c++·网络协议
小_太_阳27 分钟前
Scala_【1】概述
开发语言·后端·scala·intellij-idea
向宇it28 分钟前
【从零开始入门unity游戏开发之——unity篇02】unity6基础入门——软件下载安装、Unity Hub配置、安装unity编辑器、许可证管理
开发语言·unity·c#·编辑器·游戏引擎
古希腊掌管学习的神1 小时前
[LeetCode-Python版]相向双指针——611. 有效三角形的个数
开发语言·python·leetcode
赵钰老师1 小时前
【R语言遥感技术】“R+遥感”的水环境综合评价方法
开发语言·数据分析·r语言
就爱学编程1 小时前
重生之我在异世界学编程之C语言小项目:通讯录
c语言·开发语言·数据结构·算法
Oneforlove_twoforjob2 小时前
【Java基础面试题025】什么是Java的Integer缓存池?
java·开发语言·缓存
emoji1111112 小时前
前端对页面数据进行缓存
开发语言·前端·javascript