详解 Scala 的模式匹配

一、基本语法

Scala 中的模式匹配类似于 Java 中的 switch 语法,但是功能更强大

/**[var a = / def func() = ] var_name match {case value1 => statement1case value2 => statement2case value3 => statement3case _ => defaultOP}
*/
object TestMatchCase {def main(args: Array[String]): Unit = {val a = 10val b = 20def matchCalculate(op: Char): Any = op match {case '+' => a + bcase '-' => a - bcase '*' => a * bcase '/' => a / bcase _ => "非法运算符"}println(matchCalculate('+'))println(matchCalculate('*'))println(matchCalculate('@'))}
}
  • 如果所有 case 都不匹配,那么会执行 case _ 分支,类似于 Java 中 default 语句,若此时没有 case _ 分支,那么会抛出 MatchError
  • 每个 case 中,不需要使用 break 语句,会自动中断 case
  • match case 语句可以匹配任何类型,而不只是字面量
  • => 后面的代码块,直到下一个 case 语句之前的代码是作为一个整体执行,可以使用 {} 括起来,也可以省略

二、模式守卫

在模式匹配中增加条件守卫可以匹配某个范围的数据

object TestMatchGuard {def main(args: Array[String]): Unit = {// 使用模式守卫实现绝对值def abs(num: Int): Int = {num match {case i: Int if i >= 0 => icase i: Int if i < 0 => -i}}println(abs(22))println(abs(0))println(abs(-18))}
}

三、模式匹配类型

1. 匹配常量

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

object TestMatchTypes {def main(args: Array[String]): Unit = {def describe(x: Any): String = x match {case 1 => "Int one"case "hello" => "String hello"case true => "Boolean true"case '+' => "Char +"case _ => ""  // _ 是占位符,可以用其他字符替代}println(describe(1))println(describe("hello"))println(describe(0.3))}
}

2. 匹配类型

模式匹配可以实现类似 isInstanceOf[T] 和 asInstanceOf[T] 的功能

object TestMatchTypes {def main(args: Array[String]): Unit = {def describeType(x: Any): String = x match {case i: Int => "Int " + icase s: String => "String " + scase list: List[String] => "List " + listcase array: Array[Int] => "Array[Int] " + array.mkString(",")case a => "other type " + a  // 用 _ 占位不能获取到传入的变量,可以用其他字符替代}println(describeType(1))println(describeType("hello"))println(describeType(List("hello", "scala")))println(describeType(List(2, 3))) // 可以匹配,scala 会做泛型擦除println(describeType(Array("hello", "scala"))) // 不能匹配,Array 没有泛型擦除println(describeType(Array(2, 3)))}
}

3. 匹配数组

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

object TestMatchTypes {def main(args: Array[String]): Unit = {val arrList: List[Array[Any]] = List(Array(0),Array(1, 0),Array(0, 1, 0),Array(1, 1, 0),Array(2, 3, 7 ,15),Array("hello", 20, 30))for(arr <- arrList) {var result = arr match {case Array(0) => "只有一个元素为 0 的数组"case Array(x, y) => "有两个元素的数组:" + x + ", " + ycase Array(0, _*) => "第一个元素为 0 的数组"case Array(x, 1, y) => "中间元素为 1 的三元素数组"case _ => "other type"}println(result)}}
}

4. 匹配列表

4.1 方式一
object TestMatchTypes {def main(args: Array[String]): Unit = {val lists: List[List[Any]] = List(List(0),List(1, 0),List(0, 0, 0),List(1, 1, 0),List(1, 0, 0),List(88),List("hello"))for(list <- lists) {var result = list match {case List(0) => "只有一个元素为 0 的列表"case List(x, y) => "有两个元素的列表:" + x + ", " + ycase List(0, _*) => "第一个元素为 0 的列表"case List(x, 1, y) => "中间元素为 1 的三元素列表"case List(a) => "只有一个元素的列表:" + acase _ => "other type"}println(result)}}
}
4.2 方式二
object TestMatchTypes {def main(args: Array[String]): Unit = {val list1 = List(1, 2, 3, 4, 5)val list2 = List(5)// first:1 second:2 rest:List(3, 4, 5)list1 match {case first :: second :: rest => println(s"first:$first second:$second rest:$rest") // 匹配有第一个和第二个元素的列表case _ => println("other type")}// other typelist2 match {case first :: second :: rest => println(s"first:$first second:$second rest:$rest")case _ => println("other type")}}
}

5. 匹配元组

object TestMatchTypes {def main(args: Array[String]): Unit = {val tupleList = List((0),(0, 1),(1, 1),(0, 0, 1),(1, 1, 0),("hello", true, 0.5))for(tuple <- tupleList) {val result = tuple match {case (0) => "只有一个元素 0 的元组"case (0, _) => "第一个元素为 0 的二元组"case (x, y) => "二元组:" + x + ", " + ycase (a, 1, _) => "中间元素为 1 的三元组:" + acase (a, b, c) => s"三元组:$a $b $c"case _ => "other type"}println(result)}}
}

6. 匹配对象及样例类

6.1 自定义实现
// 定义一个类
class Student(val name: String, val age: Int)// 定义伴生对象
object Student {// 实现 apply 方法用于创建对象def apply(name: String, age: Int): Student = new Student(name, age)// 实现 unapply 方法用于拆解对象属性def unapply(student: Student): Option[(String, Int)] = { // Option 防止空指针if(student == null) {None} else {Some((student.name, student.age))}}
}object TestMatchObject {def main(args: Array[String]): Unit = {// 创建对象val student = new Student("tom", 18)// 对象模式匹配val result = student match { // 对象匹配的是各属性是否相同而不是地址// 不能使用 new 创建,且类的伴生对象必须实现 apply 和 unapply 方法case Student("tom", 18) => "tom 18" case _ => "other"}println(result)}
}
  • Student("tom", 18) 在执行时,实际调用的是伴生对象中的 apply 方法构造出相应的对象
  • Student("tom", 18) 写在 case 后时会默认调用伴生对象中的 unapply 方法(对象提取器),该对象作为 unapply 方法的参数,在 unapply 方法中将该对象的 name 和 age 属性提取出来,与 new Student("tom", 18) 中的属性值进行匹配
  • 只有当 case 中对象的 unapply 方法(提取器)返回 Some,且所有属性均一致,才算匹配成功;若属性不一致,或返回 None,则匹配失败
  • 若只提取对象的一个属性,则提取器为 unapply(obj: ObjClass): Option[T];若提取对象的多个属性,则提取器为 unapply(obj: ObjClass): Option[(T1,T2,T3…)];若提取对象的可变个属性,则提取器为 unapplySeq(obj: ObjClass): Option[Seq[T]]
6.2 样例类实现
  • 样例类简介

    /**定义语法:case class className(field1: type1, field2: type2,...)特点:1.样例类仍然是类,和普通类相比,只是自动生成了伴生对象且伴生对象中自动提供了一些常用的方法,如 apply、 unapply、 toString、 equals、 hashCode 和 copy2.样例类是为模式匹配而优化的类,因为其默认提供了 unapply 方法,因此,样例类可以直接使用模式匹配,而无需自己实现 unapply 方法。3.构造器中的每一个参数默认都为 val,除非它被显式地声明为 var(不建议这样做)
    */
    case class User(name: String, age: Int) // 无需指定 val
    
  • 实现对象模式匹配

    // 定义样例类
    case class User(name: String, age: Int)object TestMatchObject {def main(args: Array[String]): Unit = {val user = User("bob", 20)val result = user match {case User("bob", 20) => "bob 20"case _ => "other"}println(result)}
    }
    

四、变量声明模式匹配

object TestMatchVariable {def main(args: Array[String]): Unit = {// 使用模式匹配元组变量赋值val (x, y, z) = (10, "hello", true)println(s"x:$x y:$y z:$z")// 使用模式匹配列表变量赋值val List(first, second, _*) = List(2, 4, 7, 9)println(s"first:$first second:$second")val fir :: sec :: rest = List(2, 4, 7, 9)println(s"fir:$fir sec:$sec rest:$rest") // fir:2 sec:4 rest:List(7, 9)// 使用模式匹配对象属性赋值val Person(name, age) = Person("zhangsan", 16)println(s"name=$name age=$age")}
}

五、for循环模式匹配

object TestMatchFor {def main(args: Array[String]): Unit = {val list: List[(String, Int)] = List(("a", 1), ("b", 4), ("c",3), ("a",4))// 1. for循环直接赋值 KVfor((k, v) <- list) { // (elem <- list)println(k + ": " + v) // println(elem._1 + ": " + elem._2)}// 2. for循环只遍历 key 或 valuefor((k, _) <- list) { // ((_, v) <- list)println(k) // println(v)}// 3. for循环遍历指定 key 或 valuefor(("a", v) <- list) { // ((k, 4) <- list)println("a: " + v) // println(k + ": 4")}}
}

六、偏函数模式匹配

偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查

1. 偏函数定义

/**val funcName: PartialFunction[inParamClass, outParamClass] = {case value => statement}
*/
// 定义一个获取列表第二个元素的偏函数
val getSecond: PartialFunction[List[Int], Option[Int]] = {case first :: second :: _ => Some(second)
}

2. 偏函数原理

// 1. 定义的偏函数会被解析翻译
val getSecond: PartialFunction[List[Int], Option[Int]] = {case first :: second :: _ => Some(second)
}
// =>
val getSecond = new PartialFunction[List[Int], Option[Int]] {//检查输入参数是否合格override def isDefinedAt(list: List[Int]): Boolean = list match {case first :: second :: _ => truecase _ => false}//执行函数逻辑override def apply(list: List[Int]): Option[Int] = list match {case first :: second :: _ => Some(second)}
}// 2. 偏函数调用时采用 applyOrElse 方法,其逻辑为 if(ifDefinedAt(list)) apply(list) else default。如果输入参数满足条件,即 isDefinedAt 返回 true,则执行 apply 方法,否则执行 defalut 方法,default 方法为参数不满足要求的处理逻辑
getSecond.applyOrElse(List(1,2,3), (_: List[Int]) => None)

3. 应用案例

object TestPartialFunction {def main(args: Array[String]): Unit = {// 使用偏函数实现绝对值方法val positiveAbs: PartialFunction[Int, Int] = {case x if x > 0 => x}val negativeAbs: PartialFunction[Int, Int] = {case x if x < 0 => -x}val zeroAbs: PartialFunction[Int, Int] = {case 0 => 0}def abs(x: Int): Int = (positiveAbs orElse negativeAbs orElse zeroAbs)(x)println(abs(-22))println(abs(18))println(abs(0))}
}

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

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

相关文章

Python——cv2 判断图像读取内容是否为空

import cv2 pic_path"xxx.jpg" imagecv2.imread(pic_path) if None image:print("图片为空") else:print("图片不为空")

ubuntu上zsh与bash切换

在Ubuntu上切换zsh和bash&#xff0c;你可以使用命令行。 切换到zsh: chsh -s $(which zsh)切换回bash: chsh -s $(which bash)注意&#xff1a;chsh命令可能需要你输入你的用户密码。 如果你想立即启动新shell&#xff0c;而不用重启&#xff0c;可以运行&#xff1a; ex…

java中的线程安全的容器

ReentrantLock 和 Condition 都是基于 AbstractQueuedSynchronizer (AQS) 实现的。 以下是 ReentrantLock 与 AQS 的关系&#xff1a; ReentrantLock 状态管理&#xff1a;ReentrantLock 通过 AQS 的 state 变量来表示锁的状态。状态为 0 表示锁未被持有&#xff0c;状态为 1…

【python】YOLO目标检测txt标签转xml,支持与原xml标签融合

需求&#xff1a; 利用现有yolo模型权重实现自动标注功能&#xff0c;即使用yolov5源码中的detect.py对待标注图像执行推理任务&#xff0c;并使用--save-txt保存标签文件&#xff0c;然后使用本文python代码直接将detect.py生成的txt标签转为xml标签&#xff0c;最后再使用la…

SAP PP学习笔记14 - MTS(Make-to-Stock) 按库存生产(策略10),以及生产计划的概要

上面讲了SAP里面的基础知识&#xff0c;BOM&#xff0c;作业手顺&#xff08;工艺路线&#xff09;&#xff0c;作业区&#xff08;工作中心&#xff09;&#xff0c;MRP&#xff0c;MPS等概念&#xff0c;现在该到用的时候了。 SAP PP学习笔记07 - 简单BOM&#xff0c;派生BO…

5门编程语言哪个好一点:深入剖析与选择指南

5门编程语言哪个好一点&#xff1a;深入剖析与选择指南 在编程的广阔领域中&#xff0c;各种语言如繁星般璀璨&#xff0c;各有其独特之处。对于初学者或寻求转型的开发者来说&#xff0c;如何选择一门合适的编程语言往往令人头疼。本文将针对五门热门编程语言进行详细剖析&am…

STC8增强型单片机进阶开发--独立按键

知不足而奋进 望远山而前行 文章目录 目录 文章目录 前言 目标 内容 原理图 按键消抖 软件设计 要求 分析 实现单个按钮 实现多个按钮 使用位操作存储状态 总结 前言 本次学习旨在探索按键操作及按键消抖的原理和实现方法。通过学习原理图、按键消抖的三种方法以及软件设计的要…

NXP RT1060学习总结 - 基础CAN功能

1、RT1060-CAN功能简介 这里使用的是RT1060系列的1064芯片进行开发&#xff0c;使用的是官方提供的开发板&#xff1b;提供的CAN外设为CAN2&#xff0c;使用CAN2的好处是IO与CAN3可以互相映射&#xff0c;而CAN3是具备CAN-FD功能。 2、CAN IO初始化 static void can2_gpio_c…

什么是Java泛型?它有什么作用

Java泛型&#xff08;Generics&#xff09;是一种允许在定义类、接口和方法时使用类型参数的机制。泛型提供了一种机制&#xff0c;使得代码可以对多种类型的对象进行操作&#xff0c;而无需进行类型转换。 Java泛型的作用 类型安全&#xff1a;通过在编译时进行类型检查&…

如何选择D类音频放大器(数字功率放大器)

1 简介 多年来&#xff0c;音频内容一直在不断发展。从当地唱片店购买 12 英寸 LP 黑胶唱片的时代已经成为过去&#xff0c;现在我们通过流式传输几乎可即时播放云端的任何内容。虽然一些音频爱好者会为了获得新奇体验而重拾黑胶唱片&#xff0c;但今天绝大多数的音频都是以数…

JVM学习笔记(持续更新)

JDK、JRE、JVM区别&#xff1f; 类加载过程 装载 验证 准备 解析 初始化 类加载器分类 双亲委派模型 如何打破双亲委派模型&#xff1f; 自定义类加载器&#xff0c;集成ClassLoader类重写loadClass,如Tomcat JVM内存模型 JVM 需要使用计算机的内存&#xff0c;Java 程序…

【LeetCode 101】对称二叉树

1. 题目 2. 分析 这道题比较经典。我又一次做错了&#xff0c;这次是花了20min都没有做出来。 最开始我的思想就是&#xff0c;递归比较左根节点的左子树和右根节点的右子树是否对称即可&#xff0c;然后觉得能解决问题了&#xff0c;便动手coding。哪知道&#xff0c;又碰到了…

电源滤波器怎么选用

电源滤波器怎么选用 滤波器应用场景及作用第一步&#xff1a;第二步&#xff1a;第三步&#xff1a;第四步&#xff1a; 滤波器应用场景及作用 可以有效解决EMC测试无法通过、端口防护、滤除干扰、设备保护等问题 主要功能有: 1、降低主电源谐波; 2、保护驱动装置电力电子元件…

算法人生(18):从神经网络的“剪枝策略”看“怎么找回时间”

IT人的工作和生活难平衡这事&#xff0c;到底要怎么解决呢&#xff0c;让我们从神经网络的“剪枝策略”中找点灵感吧&#xff01; 剪枝策略是指训练和优化深度神经网络时采取的一种技术&#xff0c;从名字就知道&#xff0c;它就像修剪树木一样&#xff0c;去除不必要的枝叶&a…

枣庄高防服务器如何确保数据的安全保护?

如何利用枣庄高防服务器确保数据的安全保护&#xff1f; 在当今信息时代&#xff0c;数据安全已经成为企业和个人都需要面对的重要问题。为了保障数据的安全&#xff0c;许多企业选择枣庄高防服务器&#xff0c;其强大的安全防护能力为用户提供了可靠的保障。而为了最大程度地…

stack和queue(2): 模拟实现

一、stack的模拟实现 stack是一个容器适配器&#xff0c;它的底层是通过对某种容器类进行封装来实现&#xff0c;标准容器list和vector&#xff0c;deque都符合这些需求&#xff0c;默认情况下&#xff0c;如果没有为stack指定底层容器就默认是使用deque实现。 我们在模拟实现…

Vuex 是什么?VueX简介

聚沙成塔每天进步一点点 本文内容 ⭐ 专栏简介Vuex 是什么核心概念1.State&#xff08;状态&#xff09;2. Getter&#xff08;获取器&#xff09;3. Mutation&#xff08;突变&#xff09;4. Action&#xff08;动作&#xff09;5. Module&#xff08;模块&#xff09; 原理解…

使用STS临时访问凭证通过客户端直连OSS对象存储服务器

目录 1、导论 2、客户端直传 3、创建RAM用户以及RAM角色 4、如何实现客户端直传 4.1、跨域访问 4.2、安全授权 5、代码示例 5.1、后端代码实例 5.2、客户端代码实例 1、导论 最近在做项目的过程中使用到了阿里云OSS来存储客户端上传的文件&#xff0c;方法是直接将客…

Keras深度学习框架实战(3):EfficientNet实现stanford dog分类

1、通过EfficientNet进行微调以实现图像分类概述 通过EfficientNet进行微调以实现图像分类&#xff0c;是一个使用EfficientNet作为预训练模型&#xff0c;并通过微调&#xff08;fine-tuning&#xff09;来适应特定图像分类任务的过程。一下是对相关重要术语的解释。 Effici…

Flutter-自定义可展开文本控件

Flutter 在移动开发中&#xff0c;常常需要处理一些长文本显示的场景&#xff0c;如何优雅地展示这些文本并允许用户展开和收起是一个常见的需求。在本文中&#xff0c;我将分享如何使用Flutter实现一个可展开和收起的文本控件。 效果 我们将实现一个可展开和收起的文本控件…