文章目录
Scala 模式匹配
一、模式匹配
Scala 中的模式匹配类似于Java中的switch语法
,但是scala 从语法中补充了更多的功能,所以更加强大
1、基本语法
模式匹配语法中,采用match
关键字声明,每个分支采用case
关键字进行声明,当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行相对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有的case都不匹配,那么会执行case…分支,类似于Java中的default
语句
2、语法说明
(1) 如果所有case都不匹配,那么会执行case_
分支,类似于Java中的default
语句,若此时没有case_
分支,那么会抛出 MatchError
(2) 每个case中,不需要使用break 语句,自动中断case
(3) match case 语句可以匹配任何类型,而不是字面量
(4) => 后面的代码块,直到下一个case 语句之前的代码是作为一个整体执行
,可以使用{}
括起来,也可以不括
3、案例示例
package Scala04_moshipipei
//模式匹配基本语法
class test01_moshi {
}
object test01_moshi{
def main(args: Array[String]): Unit = {
//1、基本语法定义
val x:Int = 10
val y:String = x match {
case 1 => "one" //如果为1那么返回的是one,中间是=> 连接
case 2 => "two"
case 3 => "there"
case _ => "这就是默认情况了,0"
}//判断x等于什么,如果x为1那么y就是one,如果x为2那么y就是two
println(y)
//2、示例;用模式匹配实现简单的二元运算
val a = 25
val b = 13
def hanshu(op:Char) = op match {
case '+' => a + b
case '-' => a - b
case '*' => a * b
case '/' => a / b
case '%' => a % b
case _ => "非法运算符"
}
println(hanshu('*'))
println(hanshu('+'))
println("\\") //传一个字符进去,还要进行转义
}
}
二、模式守卫
如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫
注意:num 就是 那个 i
,然后调用的时候传入参数进去就可以了
// 模式守卫
//求一个整数的绝对值
def fanfga(num:Int) = num match { //num就是下面的那个i
case i if i >= 0 => i //定义一个i,如果i大于等于0就返回i
case i if i < 0 => -i //如果小于0 那么输出-i,那肯定就是绝对值
}
println(fanfga(5))
println(fanfga(0))
println(fanfga(-5))
三、模式匹配类型
Scala 中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等。
推荐还是把 case _
加上,因为写的条件可能就那么几个,要是写错了,就会报 MatchError
1、匹配常量
//Scala 中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等。
//1、匹配常量
def hanshu(x:Any):String = x match { //返回值不一定非得String,其他的也可以
case 1 => "num one" //这就是返回值
case "hello scala" => "String hello"
case true => "Boolen true" //连布尔类型都可以
case '+' => "Char +"
case _ => "" //建议还是要设置一个这个,然后条件是有限的,传入参数要是没有就报错了
}
println(hanshu(1))
println(hanshu("hello scala"))
println(hanshu(true))
println(hanshu(0.3))
2、匹配类型
Scala 不仅仅可以匹配常量,还可以匹配数据结构,比如列表,数组.
注意:类型匹配的时候要注意,比如定义了List[String]
泛型,规定的定义的是String
类型的,但是Int类型的还是匹配上了,因为Scala 的底层有泛型擦除
,他只会匹配到是一个list,泛型会自动擦除。而数组比较特殊,它是没有泛型擦除的,所以特别注意这两个地方,容易出现Bug
package Scala04_moshipipei
//模式匹配类型
class test02_pipeileixing {
}
object test02_pipeileixing{
def main(args: Array[String]): Unit = {
//Scala 中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等。
//1、匹配常量
def hanshu(x:Any):String = x match { //返回值不一定非得String,其他的也可以
case 1 => "num one" //这就是返回值
case "hello scala" => "String hello"
case true => "Boolen true" //连布尔类型都可以
case '+' => "Char +"
case _ => "" //建议还是要设置一个这个,然后条件是有限的,传入参数要是没有就报错了
}
println(hanshu(1))
println(hanshu("hello scala"))
println(hanshu(true))
println(hanshu(0.3))
println("================")
//2、匹配类型
def hanshu2(x:Any):String = x match {
case i:Int => "int" + i //相当于前面规定了这个变量必须的是Int类型的才能匹配上
case i:String => "String," + i
case list: List[String] => "list:" + list //连List类型都可以定义
case array:Array[Int] => "现在是第"+array.mkString(",")+"圈" //因为数组直接输出是引用,所以要用mkString方法输出
case a => "Something else:" + a //这是兜底的方法,要拿到值的话就不能用 _ ,必要要设置一个变量
}
println(hanshu2(35))
println(hanshu2("hello"))
println(hanshu2(List("hi","hello")))
println(hanshu2(List(2,23)))
println(hanshu2(Array("hi","hello")))
println(hanshu2(Array(2,23)))
}
}
3、匹配数组
Scala 模式匹配,匹配数组非常强大,可以进行各种模糊的匹配,比如可以匹配必须是三个元素,或者中为1的三个元素的数组。
//3、匹配数组
for (arr <- List(
Array(0),
Array(1,0),
Array(0,1,0),
Array(1,1,0),
Array(2,3,7,15),
Array("hello",20,30)
)){
val result = arr match {
case Array(0) => "0"
case Array(1,0) => "1,0"
case Array(x,y) => "Array:" + x + y //相当于可以进行模糊查询,匹配两元素数组
case Array(0,_*) => "以0开头的数组"
case Array(x,1,z) => "中间为1的三元素数组"
case _ => "兜底的方法"
}
println(result)
}
4、匹配列表
列表的匹配有两种方式,第一种和数组的匹配的方式是一样的,
第二种是: case first :: second :: rest => println(s"first:${first} second:${second} rest
,rest是列表,first是第一个元素,second是第二个元素,表示这个列表中至少要有两个元素才能匹配到。列表的这种匹配方式非常常用。
//方式一
for(list <- List(
List(0),
List(1,0),
List(0,0,0),
List(1,1,0),
List(88),
List("hello")
)){
val result2 = list match { //这个list就是前面的那个list
case List(0) => "0"
case List(x,y) => "List(x,y):" + x + "," + y //这个x,y就是两个数的那个列表
case List(0,_*) => "List(0,...)" //必须以0开头的列表。后面的个数不限*是通配符
case List(a) => "List(a)" //表示list里面只有一个元素
case _ => "something else"
}
println(result2)
}
println("=====================")
//方式二
//这种方式在列表匹配里面非常常用
val list = List(1,2,5,7,24)
val list1 = List(24)
list1 match {
case first :: second :: rest => println(s"first:${first} second:${second} rest ${rest}") //相当于rest是一个列表,第一个元素是first,第二个元素是second
case _ => println("something else")
}
5、匹配元组
元组的匹配跟之前的列表和数组的匹配是非常相似的
for(tuple <- List(
(0,1),
(0,0),
(0,1,0),
(1,23,56),
("hello",true,0.5)
)){
val result = tuple match {
case (a,b) => println(s"${a},${b}") //这就是一个二元组
case (0,_) => println("0,_") //表示第一个元素是0,第二个元素不做要求的一个二元组
case (a,1,_) => println("a,1,_"+ a) //表示中间那个元素必须是1,最后一个元素不关心
case _ => "something"
}
println(result)
}
除此之外很有很多操作,很灵活
package Scala04_moshipipei
//元组匹配的扩展,元组的匹配是有一点特殊的,是很灵活的
class test03_MatchTuple {
}
object test03_MatchTuple{
def main(args: Array[String]): Unit = {
//1、在变量声明时匹配
val (x,y) = (10,"hello")
println(s"${x},${y}")
val List(first,second,_*) = List(23,15,9,78) //把first赋值给了23,吧second赋值给了15
println(s"first:${first},second:${second}")
val fir :: se :: rest = List(23,15,9,78) //想要吧后面两个赋值给一个,那么还是::这种方法
println(s"first:${fir},second:${se},rest:${rest}")
//2、for推导式中进行模式匹配
val list:List[(String,Int)] = List(("a",12),("b",35),("c",27),("a",24))
for (i <- list){ //使用增强for循环进行遍历
println(i._1 + " " + i._2)
}
//2.2 将List的元素直接定义为元组,对变量赋值
for((word,count) <- list){
println(word+ " " +count) //直接输出就可以了
}
println("=============")
//2.3 可以不考虑某个位置的变量,只遍历key或者value
for((word,_) <- list){
println(word) //这样就得到的全都是key了
}
println("==================")
//2.4 指定某个位置的值必须是多少
for (("a",count) <- list){
println(count) //把所有key是"a"对应的value值都打印出来
}
}
}
6、匹配对象及样例类
(1) 基本语法
class User(val name:String,val age:Int)
这个可以说是一个非常强大的功能,通过伴生对象里面的值来进行匹配,伴生对象里面要有apply
方法,还要有unapply
方法,用来对对象属性进行拆解,不然会报错。
步骤简单来说就是,首先定义一个类,然后定义它的伴生对象,然后再伴生对象里面实现apply
方法,然后还要是先unapply
方法
package Scala04_moshipipei
class test04_Object {
}
object test04_Object{
def main(args: Array[String]): Unit = {
val student = new Student("aex",18)
//针对对象实例的内容进行匹配
val result = student match {
case Student("aex",18) => "aex,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) //这样就拆分出来了
}
}
}
(2) 优化case样例类
上面那种方式,虽然实现了,但是太繁琐了违背了scala简洁的定义,所以有了样例类,样例类的定义非常简单,直接在类的前面加上case
关键字
例如:case class Student1(val name:String,val age:Int){}
定义了样例类之后,就相当于自动实现了伴生类,apply方apply
法和unapply
方法,然后可以直接进行对象匹配了
package Scala04_moshipipei
//样例类,对象匹配的简化
class test05_yanglilei {
}
object test05_yanglilei{
def main(args: Array[String]): Unit = {
val student = new Student1("aex",19)
val result = student match {
case Student1("aex",19) => "aex,18"
case _ => "没有可以进行匹配的"
}
println(result)
}
}
//定义样例类 前面加一个case关键字就可以了
//定义了样例类,就相当于自动实现了伴生对象,apply方法和unapply方法
case class Student1(val name:String,val age:Int){
}
7、偏函数式的模式匹配(了解)
偏函数也是函数的一种,通过偏函数我们可以方便的对输入参做更精确的检查。例如该偏函数的输入类型为List[Int],而我们需要的是第一个元素是0的集合,这就是通过模式匹配实现的。
偏函数定义
package Scala04_moshipipei
//偏函数
class test06_PartialFunction {
}
object test06_PartialFunction{
def main(args: Array[String]): Unit = {
val list = List(("a",12),("b",35),("c",27),("a",13)) //二元组
//1、map转换,实现key不变,value变成原来的2倍
val newList = list.map(a => (a._1,a._2*2)) //下滑1第一个元素不变,第二个元素*2
println(newList)
//2、模式匹配也可以做这个操作,模式匹配对元组元素赋值,实现功能
val newList2 = list.map(
a => {
a match {
case (word,count) => (word,count*2) //直接这样就赋值了
case _ => "没有匹配的"
}
}
)
println(newList2)
//3、省略lambda 表达式的写法,进行简化
val newList3 = list.map{
case (word,count) => (word,count*2)
}
println(newList3)
//偏函数的应用,求绝对值
//对输入数据分为不同的情形:正,负,0
val zheng:PartialFunction[Int,Int] = { //输入的类型是Int,返回的类型也是一个Int
case x if x > 0 => x
}
//这是负数的偏函数
val fushu:PartialFunction[Int,Int] = { //输入的类型是Int,返回的类型也是一个Int
case x if x < 0 => -x
}
//这是为0的情况
val zero:PartialFunction[Int,Int] = { //输入的类型是Int,返回的类型也是一个Int
case 0 => 0
}
def abs(x:Int):Int = (zheng orElse fushu orElse zero ) (x) //前面是作为一个整体
println(abs(-5))
println(abs(0))
println(abs(35))
}
}