十一、Scala的模式匹配、偏函数、正则表达式、异常处理和提取器

本文详细介绍了Scala中的模式匹配机制,包括简单模式匹配、类型匹配、守卫、匹配样例类、匹配集合等,以及Option类型、偏函数、正则表达式和异常处理。通过实例展示了如何在不同场景下运用这些特性,如处理字符串、类型检查、数据提取、异常捕获等。同时,还探讨了如何实现类的提取器以支持模式匹配,并提供了一个随机职业匹配的案例。


1. 模式匹配

Scala中有一个强大的模式匹配机制,应用非常广泛,比如:

  • 判断固定值
  • 类型查询
  • 快速获取数据

1.1 简单模式匹配

一个模式匹配包含了一些了备选项,每个备选项都开始于关键字case,且每个备选项都包含了一个模式及一到多个表达式。箭头符号=>隔开了模式和表达式。

格式:

变量 match {
	case "常量1" => 表达式1
	case "常量2" => 表达式2
	case "常量3" => 表达式3
	case _ => 表达式4 //默认匹配项
}

执行流程:
在这里插入图片描述

示例:
在这里插入图片描述

import scala.io.StdIn

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.提示用户录入一个单词
		println("请录入一个字符串:")
		val s = StdIn.readLine()

		//2.判断该单词是否能够匹配单词
		val result = s match {
			case "hadoop" => "大数据分布式存储和计算框架"
			case "zookeeper" => "大数据分布式协调服务框架"
			case "spark" => "大数据分布式内存计算框架"
			case _ => "未匹配"
		}
		//3.打印结构
		println(result)

		//4.简写形式
		s match {
			case "hadoop" => println("大数据分布式存储和计算框架")
			case "zookeeper" => println("大数据分布式协调服务框架")
			case "spark" => println("大数据分布式内存计算框架")
			case _ => println("未匹配")
		}
	}
}

1.2 匹配类型

除了匹配数据,match表达式还可以进行类型匹配。如我们要根据不同的数据类型,来执行不同的逻辑。也可以使用match表达式来实现。

格式:

变量 match {
	case 变量名1:类型1 => 表达式1
	case 变量名2:类型2 => 表达式2
	case 变量名3:类型3 => 表达式3
	case _ => 表达式4 //默认匹配项
}

注意:
当进行case校验时,变量没有在表达式中被使用,则通过下划线来代替变量名

示例:
在这里插入图片描述

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.定义一个变量为Any类型
		val a:Any = "hadoop"

		//2.定义模式匹配
		val result1 = a match {
			case x:String => s"${x} 是String类型"
			case x:Int => s"${x} 是Int类型"
			case x:Double => s"${x} 是Double类型"
			case _ => "未匹配"
		}
		//3.打印结构
		println(result1) //hadoop 是String类型

		//4.简写形式:当进行case校验时,变量没有在表达式中被使用,则通过下划线来代替变量名
		val result2 = a match {
			case _:String => "String"
			case _:Int => "Int"
			case _:Double => "Double"
			case _ => "未匹配"
		}
		//5.打印结构
		println(result2) //Double
	}
}

1.3 守卫

守卫指的是在case语句中添加if条件判断,使得我们代码更简洁

格式:

变量 match {
	case 变量名 if条件1 => 表达式1
	case 变量名 if条件2 => 表达式2
	case 变量名 if条件3 => 表达式3
	case _ => 表达式4 
}

示例:
在这里插入图片描述

import scala.io.StdIn

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.从控制台读入一个数字
		println("录入一个整数:")
		val a = StdIn.readInt()

		//2.定义模式匹配
		a match {
			case x if x >= 0 && x <= 3 => println("[0-3]")
			case x if x >= 4 && x <=8 => println("[4-8]")
			case _ => println("未匹配")
		}
	}
}

1.4 匹配样例类

Scala中可以使用模式匹配来匹配样例类,从而实现快速获取样例类中的成员数据。在开发Akka中会涉及。

格式:

对象名 match {
	case 样例类型1(字段1, 字段2, 字段n) => 表达式1
	case 样例类型2(字段1, 字段2, 字段n) => 表达式2
	case 样例类型3(字段1, 字段2, 字段n) => 表达式3
	case _ => 表达式4 
}

注意:

  • 样例类型后的小括号中,编写的字段个数要和该样例类的字段个数保持一致
  • 通过match进行模式匹配时,要匹配的对象必须声明为:Any类型

示例:
在这里插入图片描述

object ClassDemo {
	//1.创建两个样例类Customer和Order
	case class Customer(name:String, age:Int)
	case class Order(id:Int)

	def main(args:Array[String]):Unit = {
		//2.分别定义两个样例类的对象,并指定为Any类型
		val c:Any = Customer("lee", 23)

		//3.模式匹配这两个对象
		c match {
			case Customer(a, b) => println("Customer类对象")
			case Order(a) => println("Order类对象")
			case _ => println("未匹配")
		}
	}
}
//Customer类对象

1.5 匹配集合

Scala的模式匹配还能用来匹配数组、元组、集合(列表、集、映射)等。

示例一:匹配数组
在这里插入图片描述

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.定义三个数组
		val arr1 = Array(1, 2, 3)
		val arr2 = Array(0)
		val arr3 = Array(0, 1, 2, 3, 4, 5)

		//2.模式匹配
		arr1 match {
			case Array(1, x, y) => println("第一类数组")
			case Array(0) => println("第二类数组")
			case Array(0, _*) => println("第三类数组") //下划线表示任意多个元素
			case _ => println("未匹配")
		}
	}
}
//第一类数组

示例二:匹配列表
在这里插入图片描述

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.定义三个数组
		val list1 = List(0)
		val list2 = List(0, 1, 2, 3)
		val list3 = List(11, 22)

		//2.模式匹配
		//思路一:通过List()实现
		list1 match {
			case List(0) => println("第一类列表")
			case List(0, _*) => println("第二类列表")
			case List(x, y) => println("第三类列表") 
		}
		//思路二:通过关键字实现,Nil,tail
		list1 match {
			case 0::Nil => println("第一类列表")
			case 0::tail => println("第二类列表")
			case x::y::Nil => println("第三类列表") 
		}
	}
}
//第一类列表

示例三: 匹配元组
在这里插入图片描述

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.定义元组
		val tuple1 = (1, 2, 3)
		val tuple2 = (3, 4, 5)

		//2.模式匹配
		//思路一:通过List()实现
		tuple1 match {
			case (1, x, y) => println("第一类元组")
			case (x, y, 5) => println("第二类元组")
			case _ => println("未匹配") 
		}
	}
}
//第一类元组

1.6 变量声明中的模式匹配

在定义变量时,可以使用模式匹配快速获取数据,例如:快速从数组,列表中获取数据

示例:
在这里插入图片描述

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.
		var arr = (0 to 10).toArray
		val Array(_, x, y, z, _*) = arr1
		println(x, y, z) //(1, 2, 3)

		//2.
		var list1 = (0 to 10).toList
		//思路一:通过List()实现
		val List(a, b, _*) = list1
		//思路二:通过关键字实现
		val c::d::tail = list1
		println(a, b) //(0, 1)
		println(c, d) //(0, 1)
	}
}

1.7 匹配for表达式

Scala中还可以使用模式匹配来匹配for表达式,从而实现快速获取指定数据。

示例:
在这里插入图片描述

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.
		val map1 = Map("张三" -> 23, "李四" -> 24, "王五" -> 23, "赵六" -> 26, )

		//2.
		//思路一:通过if()语句实现
		for((k, v) <- map if v == 23) println(k, v)

		//思路二:通过固定值实现
		for((k, 23) <- map) println(k, 23)
	}
}
//(张三,23)
//(王五,23)

2. Option类型

实际开发中,在返回一些数据时,难免会遇到空指针异常(NullPointerException),遇到依次就处理依次相对来说还是比较繁琐的。所以在Scala中,我们返回某些数据时,可以返回一个Option类型的对象来封装具体的数据,从而实现有效的避免空指针异常。

格式:
在这里插入图片描述

示例:
在这里插入图片描述

object ClassDemo {
	//1.定义一个两个数相除的方法,使用Option类型来封装结果
	def divide(a:Int, b:Int) = {
		if (b == 0) //除数为零
			None
		else
			Some(a / b)
	}
	
	def main(args:Array[String]):Unit = {
		//2.打印结果
		//思路一:普通实现
		val result1 = divide(10/0)
		println(result1) //None

		//思路二:通过模式匹配实现
		result1 match {
			case Some(x) => println(s"商为:${x}")
			case None(x) => println("除数不能为零")
		//除数不能为零

		//思路三:通过getOrElse()方法实现
		println(result1.getOrElse("除数不能为零"))
		//除数不能为零
		
		}
	}
}

3. 偏函数

偏函数指被包在花括号内没有match的一组case语句,它时PartialFunction[A, B]类型的一个实例对象,其中A代表输入参数类型,B代表返回结果类型
偏函数提供了更简洁的语法,可以简化函数的定义。配合集合的函数式编程,可以让代码更简洁。

格式:

val 对象名 = { //这对大括号及其内部的一组case语句,组成了一个偏函数
	case1 => 表达式1
	case2 => 表达式2
	case3 => 表达式3
	...
}

示例一:入门案例
在这里插入图片描述

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.定义偏函数
		val pf:PartialFunction[Int,String] = {
			case 1 => "一"
			case 2 => "二"
			case 3 => "三"
			case _ => "未匹配"
		}
		
		//2.调用偏函数,打印结果
		println(pf(1)) //一
		println(pf(2)) //二
		println(pf(3)) //三
		println(pf(4)) //未匹配
	}
}

示例二:结合map函数使用
在这里插入图片描述

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.定义一个列表
		val list1 = List(1 to 10).toList

		//2.转换
		val list2 = list1.map{
			case x if x >= 1 && x <= 3 => "[1-3]"
			case x if x >= 4 && x <= 8 => "[4-8]"
			case _ => "(8-*]"

		//3.打印结果
		println(list2) //List([1-3], [1-3], [1-3], [4-8], [4-8], [4-8], [4-8], [4-8],(8-*], (8-*])

		}
	}
}

4. 正则表达式

正则表达式指正确的,符合特定规则的式子,它是一门独立的语言,并能被兼容到绝大多数的编程语言中。在Scala中,可以方便地使用正则表达式来匹配数据,具体如下:

  1. Scala 提供了 Regex 类定义正则表达式
  2. 要构造一个 Regex 对象直接使用 String 类的 r 方法即可
  3. 建议使用三个双引号表示正则表达式,不需要对其中的反斜杠转义

格式:

val 正则对象名 = """具体的正则表达式""".r

注意:
使用findAllMatchIn方法可以获取到所有正则匹配到的数据(字符串)

示例一:校验邮箱是否合法
在这里插入图片描述

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.定义一个字符串,表示邮箱
		val email = "qq123@163.com"

		//2.定义一个正则表达式,来匹配邮箱是否合法
		/*
			.表示任意字符
			+表示前边的内容至少出现一次
			@表示这里必须是@号
			\.表示转义,取消.的特殊功能,把它当作普通符合
		*/
		val regex = """.+@.+\..+""".r

		//3.打印结果
		/*
			regex表示正则对象
			email表示要被校验的邮箱
			(regex.findAllMatchIn(email)表示从email字符串中,获取所有满足regex规则的字符串
		*/
		if(regex.findAllMatchIn(email).size != 0){
			//走到这里,说明是合法的邮箱
			println("合法")
		} else {
		println("非法")
		} 
	}
}
//合法

示例二:过滤所有不合法邮箱
在这里插入图片描述

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.定义列表,记录所有的邮箱
		val emailList = List("38123845@qq.com","alda88123@gmail.com","zhansan@163.com","123afadff.com")

		//2.定义一个正则表达式,来校验邮箱
		/*
			.表示任意字符
			+表示前边的内容至少出现一次
			@表示这里必须是@号
			\.表示转义,取消.的特殊功能,把它当作普通符合
		*/
		val regex = """.+@.+\..+""".r
		
		//3.通过filter方法,用来过滤所有不合法的邮箱
		//val filterList = emailList.filter(x => regex.findAllMatchIn(x).size == 0
		//简化:
		val filterList = emailList.filter(regex.findAllMatchIn(_).size == 0
		
		//4.打印结果
		println(filterList)
		
	}
}
//List("38123845@qq.com","alda88123@gmail.com","zhansan@163.com")

示例三:获取邮箱运营商

在这里插入图片描述

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.定义列表,记录所有的邮箱
		val emailList = List("38123845@qq.com","alda88123@gmail.com","zhansan@163.com","123afadff.com")


		val regex = """.+@(.+)\..+""".r
		
		//3.通过模式匹配,获取邮箱的运营商
		val list2 = emailList.map{
			case x @ regex(company) => x -> company
			case x => x -> "未匹配"
		}
		
		//4.打印结果
		println(list2)
		
	}
}
//List((38123845@qq.com,qq),(alda88123@gmail.com,gmail),(zhansan@163.com,163),(123afadff.com,未匹配))

5. 异常处理

Scala中有两种异常处理形式:捕获异常(该方式处理完异常后,程序会继续执行)和抛出异常(该方式处理完异常后,程序会终止执行)

5.1 捕获异常

格式:

try {
	//	可能会出现问题的代码
}
catch {
	case ex:异常类型1 => //代码
	case ex:异常类型2 => //代码
}
finally {
	//代码
}

注意:

  • try中的代码是我们编写的业务处理代码
  • catch中表示当某个异常时,需要执行的代码
  • finally中写的是不管是否出现异常都会执行的代码(比如资源释放)

5.2 抛出异常

格式:

throw new Exception("这里写异常的描述信息")

示例:
在这里插入图片描述

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.通过try.catch来处理 除数为零异常
		try{
			//可能出现问题的代码
			val i = 10 / 0
		} catch {
			//出现问题后的解决方案
			//case ex:ArithmeticException => println("算数异常")
			case ex:Exception => ex.printStackTrace() //将异常类型,异常的描述信息,以及异常出现的位置打印到控制台上
		} finally {
			//这里用来释放资源
			println("释放资源")
		}

		
		//2.在main方法中抛出一个异常
		throw new Exception("新异常")
		println("hi") //不会被执行
		
	}
}
//算数异常
//释放资源

6. 提取器(Extractor)

不是所有的类都像样例类一样能进行某些模式匹配。因此,一个类要想支持模式匹配,必须实现一个提取器。

注意:

  • 提取器指的就是unapply()方法
  • 样例类自动实现了apply()、unapply()方法,无需我们手动定义

格式:
要实现一个类的提取器,只需要在该类的伴生对象中实现一个unapply方法即可。

def unapply(stu:Student):Option[(类型1,类型2,类型3...)] = {
	if(stu != null){
		Some((变量1,变量2,变量3...))
	}
	else {
		None
	}
}

在这里插入图片描述

示例:
在这里插入图片描述

object ClassDemo {
	//1.创建一个Student类,包含姓名和年龄两个字段
	class Student(var name:String, var age:Int)

	//2.实现一个类的提取器,并使用match表达式进行模式匹配,提取类中的字段
	object Student{
		//apply() 根据给定的字段,将其封装成一个Student类型的对象
		def apply(name:String, age:Int) = new Student(name, age)
		
		//unapply() 根据传入的学生对象,获取其对应的各个属性值
		def unapply(s:Student) = {
			if(s == null)
				None
			else
				Some(s.name, s.age)
		}
	}
	
	def main(args:Array[String]):Unit = {
		//3.创建学生类的对象
		//方式一:普通写法
		val s1 = new Student("lee", 23) 
		
		//方式二:免new
		val s2 = Student("mike", 24)
		
		//4.获取对象中的各个属性值,然后打印
		//方式一:普通获取
		println(s1.name, s1.age) //(lee,23)
		//方式二:unapply()方法
		val result = Student.unapply(s1)
		println(result) //Some((lee,23))
		//方式三:通过模式匹配获取
		s1 match {
			case Student(name, age) => println(s"${name}, ${age}")
			case _ => println("未匹配")
		}
		//lee,23
		
	}
}

7. 案例:随机职业

需求:
在这里插入图片描述

import scala.io.StdIn

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.提示用户录入整数,并接收
		println("录入一个整数1-5:")
		val num = StdIn.readInt()
		
		//2.匹配对应的职业
		val occupation = num match {
			case 1 => "一品带刀侍卫"
			case 2 => "宰相"
			case 3 => "江湖郎中"
			case 4 => "打铁匠"
			case 5 => "店小二"
			case 6 => "不详"
		
		//3.打印结果
		println(occupation)
		
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值