当前位置: 首页 > news >正文

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

模式匹配

  • 第 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)

相关文章:

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

模式匹配第 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 章 模式匹配 Scal…...

Linux:基于libevent读写管道代码

基于libevent读写管道代码&#xff1a; 读端&#xff1a; #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <event2/event.h> #include…...

2022年中职网络安全逆向题目整理合集

中职网络安全逆向题目整理合集逆向分析&#xff1a;PE01.exe算法破解&#xff1a;flag0072算法破解&#xff1a;flag0073算法破解&#xff1a;CrackMe.exe远程代码执行渗透测试天津逆向re1 re2逆向分析&#xff1a;PE01.exe FTPServer20220509(关闭链接)  FTP用户名:PE01密码…...

Tencent OS下逻辑卷(LVM)增加硬盘扩容

上一篇文章写了逻辑卷创建以及使用剩余空间为已经创建的逻辑卷扩容。 本篇是针对卷组空间已经用尽时的扩容方法。那就是增加硬盘。 首先我们为虚拟机增加硬盘/dev/sdd 使用fdisk为/dev/sdd分区,方法在上一篇文章已经描述,在此不再赘述。 新增的硬盘使用如下命令添加到卷组…...

【Java】Spring的创建和使用

Spring的创建和使用 Spring就是一个包含众多工具方法的IOC容器。既然是容器&#xff0c;那么就具备两个最主要的功能&#xff1a; 将对象存储到容器中从容器中将对象取出来 在Java语言当中对象也叫作Bean。 1. 创建Spring项目 创建一个普通maven项目添加Spring框架支持(spri…...

【HTML】HTML 表单 ④ ( textarea 文本域控件 | select 下拉列表控件 )

文章目录一、textarea 文本域控件二、select 下拉列表控件一、textarea 文本域控件 textarea 文本域 控件 是 多行文本输入框 , 标签语法格式如下 : <textarea cols"每行文字字符数" rows"文本行数">多行文本内容 </textarea>实际开发中 并不…...

MySQL 操作 JSON 数据类型

MySQL 从 v5.7.8 开始支持 JSON 数据类型。 JSON 数据类型和传统数据类型的操作还是有很大的差别&#xff0c;需要单独学习掌握。好在 JSON 数据类型的学习成本不算太高&#xff0c;只是在 SQL 语句中扩展了 JSON 函数&#xff0c;操作 JSON 数据类型主要是对函数的学习。 新…...

关于vue3生命周期的使用、了解以及用途(详细版)

生命周期目录前言组合式写法没有 beforeCreate / created 生命周期&#xff0c;并且组合式写生命周期用哪个先引哪个beforeCreatecreatedbeforeMount/onBeforeMountmounted/onMountedbeforeUpdate/onBeforeUpdateupdated/onUpdatedbeforeUnmount/onBeforeUnmountunmounted/onUn…...

2月,真的不要跳槽。

新年已经过去&#xff0c;马上就到金三银四跳槽季了&#xff0c;一些不满现状&#xff0c;被外界的“高薪”“好福利”吸引的人&#xff0c;一般就在这时候毅然决然地跳槽了。 在此展示一套学习笔记 / 面试手册&#xff0c;年后跳槽的朋友可以好好刷一刷&#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…...

51单片机学习笔记_12 LCD1602 原理及其模块化代码

LCD1602 liquid crystal display 液晶显示屏&#xff0c;一种字符型液晶显示模块&#xff0c;可以显示 16*2 个字符&#xff0c;每个字符是 5*7 点阵。 P0 P2 会和数码管、LED 一定程度上冲突。 地。 Vcc。 调对比度的。 RS&#xff1a;数据指令端。1代表 DB 是数据&#x…...

科技 “新贵”ChatGPT 缘何 “昙花一现” ,仅低代码风靡至今

恍惚之间&#xff0c;ChatGPT红遍全网&#xff0c;元宇宙沉入深海…… 在科技圈&#xff0c;见证了太多“昙花一现”&#xff0c;“新贵” ChatGPT 的爆火几乎复制了元宇宙的路径&#xff0c;它会步元宇宙的后尘&#xff0c;成为下一个沉入深海的工具吗&#xff1f; 不可否认的…...

redis基本入门| 怎么安装redis?什么的是redis?怎么使用?

目录 一、Redis下载与安装 二、基本概念 1.什么是Redis? 2.Redis端口多少&#xff1f; 3.Redis是单线程还是多线程&#xff1f; 4.Redis为什么单线程还这么快&#xff1f; 三、Redis的基本操作 四、Redis的五个基本类型 1.Redis-key 2.字符串 string 3.列表 list …...

kubernetes traefik ingress 安装部署以及使用和注意点

1、简介 Traefik 是一款 open-source 边缘路由器&#xff0c;可让您轻松地发布服务. 它接收来自您的系统请求&#xff0c;并找出负责处理它们的后端服务组件。 traefik 与众不同在于它能够自动发现适合您服务的配置。 当 Traefik 检查您的基础设施时&#xff0c;它会发现相关信…...

电脑病毒已灭绝,是真的吗?

大家有没有这样一个疑问&#xff0c;觉得自己的电脑好像很久没有电脑病毒了&#xff1f;之前大名鼎鼎的蠕虫2000&#xff0c;熊猫烧香都变得不那么常见了。到底是电脑因为自身优化和杀毒软件的防护导致病毒变少了&#xff0c;还是本身电脑病毒变少了呢&#xff1f;&#xff08;…...

基于RK3399+Linux QT地面测试台多参数记录仪测试平台软件设计(二)

rk3399 是由本土芯片厂商瑞芯微&#xff08;Rockchip&#xff09;研发的高性能、低功耗“中国芯”。在 2016 年 4 月&#xff0c;rk3399 首次在香港举行的电子展上亮相。芯片使用六核大 LITTLE 处理器&#xff1a; 包括四核的 Cortex-A53 和双核的 Cortex-A72&#xff0c;主频可…...

追梦之旅【数据结构篇】——详解C语言实现动态版顺序栈

详解C语言动态实现顺序栈~&#x1f60e;前言&#x1f64c;预备小知识&#x1f49e;栈的概念及结构整体实现内容分析&#x1f49e;1.头文件编码实现&#x1f64c;2.功能文件编码实现&#x1f64c;3.测试文件的编写&#xff1a;&#x1f64c;总结撒花&#x1f49e;&#x1f60e;博…...

Ubuntu 使用Nohup 部署/启动/关闭程序

目录 一、什么是nohup&#xff1f; 二、nohup能做什么&#xff1f; 三、nohup如何使用&#xff1f; 四、怎么查看/关闭使用nohup运行的程序&#xff1f; 命令 实例 一、什么是nohup&#xff1f; nohup 命令运行由 Command参数和任何相关的 Arg参数指定的命令&#xff0c…...

Spring 用到了哪些设计模式

关于设计模式&#xff0c;如果使用得当&#xff0c;将会使我们的代码更加简洁&#xff0c;并且更具扩展性。本文主要讲解Spring中如何使用策略模式&#xff0c;工厂方法模式以及Builder模式。1. 策略模式关于策略模式的使用方式&#xff0c;在Spring中其实比较简单&#xff0c;…...

Linux上基于PID找到对应的进程名以及所在目录

Linux上基于PID找到对应的进程名以及所在目录前言找到进程的pid通过top命令查看通过 ps -ef |grep nignx进行查看通过端口号进行查看查看nginx进程目录前言 在一台新接触的服务器&#xff0c;却不熟悉搭建所在目录的时候&#xff0c;这时候就就可以通过ps查找进程&#xff0c;并…...

jvm知识点与面试题

jvm 1. 定义&#xff1a;Java虚拟机&#xff08;Java virtual machine&#xff09;&#xff0c;一种能够运行Java字节码的虚拟机。 1.1. Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。 2. jvm基本结构&#xff1a; 2.1. 1 类加载…...

算法前缀和—Java版

前缀和概念 假设有数组 A[1,2,3,4,5,6,7] 为原数组&#xff0c;有数组 B作为A的前缀和数组&#xff0c;那么B[1,3,6,10,15,21,28]&#xff1b;可以发现B[i] A[0]....A[i]&#xff0c;即B[i]是数组A的前面i个数的总和。可以前缀和表示如下公式&#xff1a; B[i]∑j0iA[j]B[i]\s…...

拨开迷雾 看见vivo穿越周期的秘密

文|智能相对论作者|佘凯文任何一个行业都有周期性&#xff0c;就好像我们在做股票投资的时候&#xff0c;提到最多的就是周期规律&#xff0c;因为只有掌握规律才可以让我们赚到钱。所以不论是哪家公司都逃脱不了行业周期的宿命。行业寒冬方显强者本色就拿手机行业来说吧&#…...

浅谈常用的日志框架

文章目录1.为什么需要日志框架2.常见日志框架2.1.日志框架介绍2.2.市面上的日志框架3.Slf4j使用3.1.如何在系统中使用SLF4j3.2.可能存在的问题4.SpringBoot日志的默认配置5.SpringBoot指定日志文件6.切换日志框架1.为什么需要日志框架 通过日志的方式记录系统运行的过程或错误以…...

字节是真的难进,测开4面终上岸,压抑5个月,终于可以放声呐喊

这次字节的面试&#xff0c;给我的感触很深&#xff0c;意识到基础的重要性。一共经历了五轮面试&#xff1a;技术4面&#xff0b;HR面。 下面看正文 本人自动专业毕业&#xff0c;压抑了五个多月&#xff0c;终于鼓起勇气&#xff0c;去字节面试&#xff0c;下面是我的面试过…...

Bash初识

Bash初识 1.简介: 一.什么是shell&#xff1f; 用过计算机的人知道&#xff0c;我只要点点鼠标计算机就能按照我们的要求来进行相应的操作&#xff0c;那么&#xff0c;你有没有想过计算机为什么能够识别我们的操作呢&#xff1f;俗话说&#xff0c;人有人语&#xff0c;机有机…...

ElasticSearch Script 操作数据最详细介绍

文章目录ElasticSearch Script基础介绍基础用法List类型数据新增、删除nested数据新增、删除根据指定条件修改数据根据指定条件修改多个字段数据-查询条件也使用脚本根据指定条件删除nested中子数据数据根据条件删除数据删除之后结果创建脚本&#xff0c;通过脚本调用根据条件查…...

【黑盒模糊测试】路由器固件漏洞挖掘实战--AFL++ qemu_mode

前言 很久之前就想写AFL++的qemu_mode了,只是模糊测试专题的文章有些过于耗费时间,加上工作原因导致一直搁置。最近需要出差会用到黑盒模糊测试,所以就当做复习一遍,我记得Fuzzing 101也有一个qemu_mode的练习,有空的话下一篇文章更新吧~ 编写不易,如果能够帮助到你,希望…...

【java实现Word模板导出】Xdocreport和Freemaker

如果只是生成简单的word文件的话可以使用 Hutool 上手简单使用方便。 但如果需要导出内容比较复杂的word文件的话用那个就不合适了&#xff0c;这时候就需要Xdocreport这玩意了。 制作模板 新建一个word文档在需要插入变量的地方使用快捷键 Crtl F9 来生成一个域 然后右键单…...

Stable-Baselines 3 部分源代码解读 3 ppo.py

Stable-Baselines 3 部分源代码解读 ./ppo/ppo.py 前言 阅读PPO相关的源码&#xff0c;了解一下标准库是如何建立PPO算法以及各种tricks的&#xff0c;以便于自己的复现。 在Pycharm里面一直跳转&#xff0c;可以看到PPO类是最终继承于基类&#xff0c;也就是这个py文件的内…...