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
相加。
需要注意的是,在模式匹配中,每个匹配分支都必须满足类型一致性和互斥性,以避免出现匹配二义性的情况。
模式守卫
- 说明
如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。 - 代码示例
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
,包含两个元素:一个字符串和一个整数。然后,我们使用模式匹配的方式将元组中的值解构并赋给了两个新的变量 str
和 num
。这样,我们就可以通过这两个变量来访问元组中的值。
变量声明时模式匹配案例
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方法,还实现了equals
、hashCode
、toString
等方法。
它具有许多有用的功能,使得数据建模和处理更加简洁和方便。
以下是样例类的特点和使用方式:
-
自动创建类的伴生对象:定义样例类时,编译器会自动生成一个与类同名的伴生对象,其中包含了一些常用的方法和属性。
-
不可变性:样例类的实例默认是不可变的,即无法在创建后修改其状态。
-
默认实现了
equals
、hashCode
、toString
等方法:编译器会自动生成这些方法的实现,使得比较、哈希和输出对象更加方便。 -
模式匹配的支持:样例类通常用于模式匹配,可以方便地匹配和提取类的属性。
下面是一个简单的示例来说明如何定义和使用样例类:
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
的样例类。它有两个属性:name
和age
。使用样例类的主构造函数可以直接创建对象,而不需要使用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
样例类及两个子类 Apple
和 Banana
,还有一个对象 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 类的实例作为首都。通过嵌套的模式匹配,我们可以提取和打印出国家的首都。