Scala模式匹配详解(第八章:基本语法、模式守卫、模式匹配类型)(尚硅谷笔记)

news/2024/3/29 22:26:31/文章来源:https://blog.csdn.net/Argonaut_/article/details/129167212

模式匹配

  • 第 8 章 模式匹配
    • 8.1 基本语法
    • 8.2 模式守卫
    • 8.3 模式匹配类型
      • 8.3.1 匹配常量
      • 8.3.2 匹配类型
      • 8.3.3 匹配数组
      • 8.3.4 匹配列表
      • 8.3.5 匹配元组
      • 8.3.6 匹配对象及样例类
    • 8.4 变量声明中的模式匹配
    • 8.5 for 表达式中的模式匹配
    • 8.6 偏函数中的模式匹配(了解)


第 8 章 模式匹配

Scala 中的模式匹配类似于 Java 中的 switch 语法

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 从语法中补充了更多的功能,所以更加强大。

8.1 基本语法

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

object TestMatchCase {def main(args: Array[String]): Unit = {var a: Int = 10var b: Int = 20var operator: Char = 'd'var result = operator match {case '+' => a + bcase '-' => a - bcase '*' => a * bcase '/' => a / bcase _ => "illegal"}println(result)}
}

1)说明

  • (1)如果所有 case 都不匹配,那么会执行 case _ 分支,类似于 Java 中 default 语句, 若此时没有 case _
    分支,那么会抛出 MatchError。
  • (2)每个 case 中,不需要使用 break 语句,自动中断 case。
  • (3)match case 语句可以匹配任何类型,而不只是字面量。
  • (4)=> 后面的代码块,直到下一个 case 语句之前的代码是作为一个整体执行,可以 使用{}括起来,也可以不括。

8.2 模式守卫

1)说明

如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。

2)案例实操

object TestMatchGuard {def main(args: Array[String]): Unit = {def abs(x: Int) = x match {case i: Int if i >= 0 => icase j: Int if j < 0 => -jcase _ => "type illegal"}println(abs(-5))}
}

8.3 模式匹配类型

8.3.1 匹配常量

1)说明

Scala 中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等。

2)实操

object TestMatchVal {def main(args: Array[String]): Unit = {println(describe(6))}def describe(x: Any) = x match {case 5 => "Int five"case "hello" => "String hello"case true => "Boolean true"case '+' => "Char +"}
}

8.3.2 匹配类型

1)说明

需要进行类型判断时,可以使用前文所学的 isInstanceOf[T]和 asInstanceOf[T],也可使
用模式匹配实现同样的功能。
2)案例实操

object TestMatchClass {def describe(x: Any) = x match {case i: Int => "Int"case s: String => "String hello"case m: List[_] => "List"case c: Array[Int] => "Array[Int]"case someThing => "something else " + someThing}def main(args: Array[String]): Unit = {//泛型擦除println(describe(List(1, 2, 3, 4, 5)))//数组例外,可保留泛型println(describe(Array(1, 2, 3, 4, 5, 6)))println(describe(Array("abc")))}
}

8.3.3 匹配数组

1)说明

scala 模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素
为 0 的数组。

2)案例实操

object TestMatchArray {def main(args: Array[String]): Unit = {for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), 
Array(1, 1, 0), Array(1, 1, 0, 1), Array("hello", 90))) { // 对
一个数组集合进行遍历val result = arr match {case Array(0) => "0" //匹配 Array(0) 这个数组case Array(x, y) => x + "," + y //匹配有两个元素的数
组,然后将将元素值赋给对应的 x,ycase Array(0, _*) => "以 0 开头的数组" //匹配以 0 开头和
数组case _ => "something else"}println("result = " + result)}}
}

8.3.4 匹配列表

1)方式一

object TestMatchList {def main(args: Array[String]): Unit = {//list 是一个存放 List 集合的数组//请思考,如果要匹配 List(88) 这样的只含有一个元素的列表,并原值返.应该怎么写for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 
0, 0), List(88))) {val result = list match {case List(0) => "0" //匹配 List(0)case List(x, y) => x + "," + y //匹配有两个元素的 Listcase List(0, _*) => "0 ..."case _ => "something else"}println(result)}}
}

2)方式二

object TestMatchList {def main(args: Array[String]): Unit = {val list: List[Int] = List(1, 2, 5, 6, 7)list match {case first :: second :: rest => println(first + "-" + 
second + "-" + rest)case _ => println("something else")}}
}

8.3.5 匹配元组

object TestMatchTuple {def main(args: Array[String]): Unit = {//对一个元组集合进行遍历for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2))) {val result = tuple match {case (0, _) => "0 ..." //是第一个元素是 0 的元组case (y, 0) => "" + y + "0" // 匹配后一个元素是 0 的对
偶元组case (a, b) => "" + a + " " + bcase _ => "something else" //默认}println(result)}}
}

扩展案例

object TestGeneric {def main(args: Array[String]): Unit = {//特殊的模式匹配 1 打印元组第一个元素for (elem <- Array(("a", 1), ("b", 2), ("c", 3))) {println(elem._1)}for ((word,count) <- Array(("a", 1), ("b", 2), ("c", 3))) {println(word)}for ((word,_) <- Array(("a", 1), ("b", 2), ("c", 3))) {println(word)}for (("a",count) <- Array(("a", 1), ("b", 2), ("c", 3))) {println(count)}println("--------------")//特殊的模式匹配 2 给元组元素命名var (id,name,age): (Int, String, Int) = (100, "zs", 20)println((id,name,age))println("--------------")//特殊的模式匹配 3 遍历集合中的元组,给 count * 2var list: List[(String, Int)] = List(("a", 1), ("b", 2), ("c", 3))//println(list.map(t => (t._1, t._2 * 2)))println(list.map{case (word,count)=>(word,count*2)})var list1 = List(("a", ("a", 1)), ("b", ("b", 2)), ("c", ("c", 3)))println(list1.map{case (groupkey,(word,count))=>(word,count*2)})}
}

8.3.6 匹配对象及样例类

1)基本语法

class User(val name: String, val age: Int)
object User{def apply(name: String, age: Int): User = new User(name, age)def unapply(user: User): Option[(String, Int)] = {if (user == null)NoneelseSome(user.name, user.age)}
}
object TestMatchUnapply {def main(args: Array[String]): Unit = {val user: User = User("zhangsan", 11)val result = user match {case User("zhangsan", 11) => "yes"case _ => "no"}println(result)}
}

小结

➢ val user = User(“zhangsan”,11),该语句在执行时,实际调用的是 User 伴生对象中的
apply 方法,因此不用 new 关键字就能构造出相应的对象。

➢ 当将 User(“zhangsan”, 11)写在 case 后时[case User(“zhangsan”, 11) => “yes”],会默
认调用 unapply 方法(对象提取器),user 作为 unapply 方法的参数,unapply 方法
将 user 对象的 name 和 age 属性提取出来,与 User(“zhangsan”, 11)中的属性值进行
匹配

➢ case 中对象的 unapply 方法(提取器)返回 Some,且所有属性均一致,才算匹配成功,
属性不一致,或返回 None,则匹配失败。

➢ 若只提取对象的一个属性,则提取器为 unapply(obj:Obj):Option[T]
若提取对象的多个属性,则提取器为 unapply(obj:Obj):Option[(T1,T2,T3…)]
若提取对象的可变个属性,则提取器为 unapplySeq(obj:Obj):Option[Seq[T]]

2)样例类

  • (1)语法: case class Person (name: String, age: Int)

(2)说明

  • ○1 样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中 自动提供了一些常用的方法,如
    apply、unapply、toString、equals、hashCode 和 copy。
  • ○2 样例类是为模式匹配而优化的类,因为其默认提供了 unapply 方法,因此,样例 类可以直接使用模式匹配,而无需自己实现
    unapply 方法。
  • ○3 构造器中的每一个参数都成为 val,除非它被显式地声明为 var(不建议这样做)

(3)实操

  • 上述匹配对象的案例使用样例类会节省大量代码
case class User(name: String, age: Int)
object TestMatchUnapply {def main(args: Array[String]): Unit = {val user: User = User("zhangsan", 11)val result = user match {case User("zhangsan", 11) => "yes"case _ => "no"}println(result)}
}

8.4 变量声明中的模式匹配

case class Person(name: String, age: Int)
object TestMatchVariable {def main(args: Array[String]): Unit = {val (x, y) = (1, 2)println(s"x=$x,y=$y")val Array(first, second, _*) = Array(1, 7, 2, 9)println(s"first=$first,second=$second")val Person(name, age) = Person1("zhangsan", 16)println(s"name=$name,age=$age")}
}

8.5 for 表达式中的模式匹配

object TestMatchFor {def main(args: Array[String]): Unit = {val map = Map("A" -> 1, "B" -> 0, "C" -> 3)for ((k, v) <- map) { //直接将 map 中的 k-v 遍历出来println(k + " -> " + v) //3 个}println("----------------------")//遍历 value=0 的 k-v ,如果 v 不是 0,过滤for ((k, 0) <- map) {println(k + " --> " + 0) // B->0}println("----------------------")//if v == 0 是一个过滤的条件for ((k, v) <- map if v >= 1) {println(k + " ---> " + v) // A->1 和 c->33}}
}

8.6 偏函数中的模式匹配(了解)

偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查。例如
该偏函数的输入类型为 List[Int],而我们需要的是第一个元素是 0 的集合,这就是通过模式
匹配实现的。

1) 偏函数定义

val second: PartialFunction[List[Int], Option[Int]] = {case x :: y :: _ => Some(y)
}

在这里插入图片描述
注:该偏函数的功能是返回输入的 List 集合的第二个元素
2)偏函数原理

上述代码会被 scala 编译器翻译成以下代码,与普通函数相比,只是多了一个用于参数
检查的函数——isDefinedAt,其返回值类型为 Boolean。

val second = new PartialFunction[List[Int], Option[Int]] {//检查输入参数是否合格override def isDefinedAt(list: List[Int]): Boolean = list match 
{case x :: y :: _ => truecase _ => false}//执行函数逻辑override def apply(list: List[Int]): Option[Int] = list match 
{case x :: y :: _ => Some(y)}
}

3)偏函数使用

偏函数不能像 second(List(1,2,3))这样直接使用,因为这样会直接调用 apply 方法,而应
该调用 applyOrElse 方法,如下

  • second.applyOrElse(List(1,2,3), (_: List[Int]) => None)
  • applyOrElse 方法的逻辑为 if (ifDefinedAt(list)) apply(list) else default。如果输入参数满 足条件,即 isDefinedAt 返回 true,则执行 apply 方法,否则执行 defalut 方法,default 方法 为参数不满足要求的处理逻辑。

4) 案例实操

  • (1)需求 将该 List(1,2,3,4,5,6,“test”)中的 Int 类型的元素加一,并去掉字符串。
def main(args: Array[String]): Unit = {val list = List(1,2,3,4,5,6,"test")val list1 = list.map {a =>a match {case i: Int => i + 1case s: String =>s + 1}}println(list1.filter(a=>a.isInstanceOf[Int]))
}

(2)实操

  • 方法一: List(1,2,3,4,5,6,“test”).filter(.isInstanceOf[Int]).map(.asInstanceOf[Int] - 1).foreach(println)
  • 方法二: List(1, 2, 3, 4, 5, 6, “test”).collect { case x: Int => x + 1 }.foreach(println)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_72642.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

论文解读 | [AAAI2020] 你所需要的是边界:走向任意形状的文本定位

目录 1、研究背景 2、研究的目的 3、方法论 3.1 Boundary Point Detection Network(BPDN) 3.2 Recognition Network 3.3 Loss Functions 4、实验及结果 论文连接&#xff1a;https://ojs.aaai.org/index.php/AAAI/article/view/6896 1、研究背景 最近&#xff0c;旨在…

深度解读 | 数据资产管理面临诸多挑战,做好这5个措施是关键

日前&#xff0c;大数据技术标准推进委员会&#xff08;中国通信标准化协会下&#xff08;CCSA&#xff09;的专业技术委员会&#xff0c;简称TC601&#xff09;发布《数据资产管理实践白皮书》&#xff08;6.0 版&#xff09;&#xff08;以下简称&#xff1a;报告&#xff09…

浏览器跨域问题

跨域问题什么是跨域问题如何解决跨域问题JSONPCORS方式解决跨域使用 Nginx 反向代理使用 WebSocket跨源请求是否能携带Cookie什么是跨域问题 跨域问题指的是不同站点之间&#xff0c;使用 ajax 无法相互调用的问题。跨域问题本质是浏览器的一种保护机制&#xff0c;它的初衷是为…

LQB01位操作说明

一个字节&#xff0c;包括了8位&#xff0c;可以对其中的8位的某一位进行读或者写&#xff1b; 比如char num12,如果用十六进制表示&#xff0c;就是0x0C&#xff0c;如果二进制表示&#xff0c;就是0000 1010 位操作函数&#xff0c;主要这里介绍&#xff0c;位读和位写0&am…

【消费战略方法论】认识消费者的恒常原理(一):消费者稳态平衡原理

“消费战略”是塔望咨询基于大量的战略与营销实践经验结合心理学、经济学、传播学等相关专业学科的知识应用进行提炼与创造形成的战略方法体系。消费战略强调以消费者为导向&#xff0c;进行企业、品牌战略、品牌营销的制订和落地&#xff0c;企业经营的每个环节和输出的每个动…

轻松搭建Redis缓存高可用集群

1. 安装单机Redis 安装步骤&#xff1a; 1.1 下载redis 官网下载3.0.0版本&#xff0c;之前几的版本不支持集群模式 下载地址&#xff1a;http://download.redis.io/releases/redis-3.0.0.tar.gz 1.2 首先需要安装gcc yum install gcc 1.3 创建目录 cd /usr/mkdir soft1.…

GitHub标星30K+的Java面试八股文长啥样?

2023年的互联网行业竞争越来越严峻&#xff0c;面试也是越来越难&#xff0c;一直以来我都想整理一套完美的面试宝典&#xff0c;奈何难抽出时间&#xff0c;这套1000道的Java面试手册我整理了整整1个月&#xff0c;上传到Git上目前star数达到了30K 一、32 道 MySQL 面试题 1&…

DACS: Domain Adaptation via Cross-domain Mixed Sampling 学习笔记

DACS介绍方法Naive MixingDACSClassMix![在这里插入图片描述](https://img-blog.csdnimg.cn/ca4f83a2711e49f3b754ca90d774cd50.png)算法流程实验结果反思介绍 近年来&#xff0c;基于卷积神经网络的语义分割模型在众多应用中表现出了显著的性能。然而当应用于新的领域时&…

乐友商城学习笔记(一)

SpringCloud 什么是SpringCloud 在SpringBoot基础上构建的微服务框架固定步骤 1.引入组件的启动器2.覆盖默认配置3.在引导类上添加相应的注解 eureka 注册中心&#xff0c;服务的注册与发现服务端 1.引入服务器启动器&#xff1a;eureka-server2.添加了配置 spring.applicati…

leetcode 21~30 学习经历

leetcode 21~30 学习经历21. 合并两个有序链表22. 括号生成23. 合并K个升序链表24. 两两交换链表中的节点25. K 个一组翻转链表26. 删除有序数组中的重复项27. 移除元素28. 找出字符串中第一个匹配项的下标29. 两数相除30. 串联所有单词的子串小结21. 合并两个有序链表 将两个升…

opencv-StereoBM算法流程(二)

OpenCV BM对于处理非畸变的立体图像, 主要有以下 3 个步骤:1. 预处理滤波: 使图像亮度归一化并加强图像纹理2. 立体匹配: 沿着水平极线用 SAD 窗口进行匹配搜索3. 再滤波: 去除坏的匹配点.匹配之后, 如果左右视差检查使能了 disp12MaxDiff > 0, 还有使用cv::validateDispari…

复习知识点三:做人不能半途而废,就算躺平也要躺最舒服的那张床

目录 运算符​编辑 键盘录入: 练习:键盘输入数字并求和 练习: 算术运算符 隐式转换(自动类型提升) 强制转换 练习1: 字符串的 "" 操作 ​编辑 练习 1: 练习2: 练习3: 自增自减运算符 赋值运算符 关系运算符(比较运算符)的分类 练习: 逻辑运算符 短路逻辑运…

qt qchart学习

Qt Charts主要由QChartView、QChart、QLegend图例、坐标轴(由QAbstractAxis子类实现)、**数据源(由QAbstractSeries子类实现)**等组成使用QChart的前期准备1. Qt5.9及以上版本&#xff1b;2. .pro文件中添加QT charts3. 在使用QChart的各个控件之前&#xff0c;引用头文件并必…

Vulnhub靶场----4、DC-4

文章目录一、环境搭建二、渗透流程三、思路总结一、环境搭建 DC-4下载地址&#xff1a;https://download.vulnhub.com/dc/DC-4.zip kali&#xff1a;192.168.144.148 DC-4&#xff1a;192.168.144.152 二、渗透流程 端口扫描&#xff1a;nmap -T5 -p- -sV -sT -A 192.168.144.1…

OSI模型和网络协议简介

文章目录一、OSI七层模型1.1什么是OSI七层模型&#xff1f;1.2这个网络模型究竟是干什么呢&#xff1f;二、TCP/IP协议三、常见协议四、物联网通信协议以及MQTT4.1 物联网七大通信协议4.2 MQTT特性一、OSI七层模型 1.1什么是OSI七层模型&#xff1f; 我们需要了解互联网的本质…

MySQL进阶篇之MySQL索引

今天主要学习MySQL索引&#xff0c;不过主要是使用Linux系统使用MySQL&#xff0c;主要是先在Linux环境下按照MySQL&#xff0c;然后演示索引的相关操作&#xff0c;介绍了索引的底层结构&#xff0c;索引的分类及语法&#xff0c;索引的性能分析&#xff0c;索引的使用规则&am…

《爆肝整理》保姆级系列教程python接口自动化(二十一)--unittest简介(详解)

简介 前边的随笔主要介绍的requests模块的有关知识个内容&#xff0c;接下来看一下python的单元测试框架unittest。熟悉 或者了解java 的小伙伴应该都清楚常见的单元测试框架 Junit 和 TestNG&#xff0c;这个招聘的需求上也是经常见到的。python 里面也有单元 测试框架-unitt…

数据结构_ 堆结构与堆排序(c++ 实现 + 完整代码 )

堆结构与堆排序 文章目录堆结构与堆排序引入堆堆结构所满足的数学特性准备代码----------- 往堆中插入元素----------- 删除堆顶堆排序构建完整代码及测试动态分配版本非动态版本引入堆 二叉树 具有左孩子与右孩子的最普通的二叉树。 满二叉树 特殊的二叉树&#xff1a;每个节…

HTML课堂笔记

HTML 课堂笔记 文章目录相关概念code我的第一个html页面基本标签相关概念 1、什么是HTML&#xff1f; Hyper Text Markup Language 超文本标记语言 超文本&#xff1f;超级文本&#xff0c;例如流媒体&#xff0c;声音、视频、图片等。 标记语言&#xff1f;这种语言是由大量…

【Git】使用Git上传项目到远程仓库Gitee码云步骤详解

电脑里存放了很多项目&#xff0c;有的备份&#xff0c;有的没备份&#xff0c;如果不仔细分类管理的话&#xff0c;时间一长&#xff0c;到时看到那就会觉得非常杂乱&#xff0c;很难整理&#xff0c;这里有一个叫源代码托管&#xff0c;用过它的都知道&#xff0c;方便管理和…