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 类的实例作为首都。通过嵌套的模式匹配,我们可以提取和打印出国家的首都。

相关推荐
m0_7487080517 分钟前
C++中的观察者模式实战
开发语言·c++·算法
qq_5375626730 分钟前
跨语言调用C++接口
开发语言·c++·算法
wjs202440 分钟前
DOM CDATA
开发语言
Tingjct42 分钟前
【初阶数据结构-二叉树】
c语言·开发语言·数据结构·算法
猷咪1 小时前
C++基础
开发语言·c++
IT·小灰灰1 小时前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧1 小时前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q1 小时前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳01 小时前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾1 小时前
php 对接deepseek
android·开发语言·php