二维码
微世推网

扫一扫关注

当前位置: 首页 » 快报资讯 » 今日快报 » 正文

详解_Scala_模式匹配

放大字体  缩小字体 发布日期:2022-06-30 18:28:32    作者:田耽鉴    浏览次数:207
导读

本篇博客中我们将采用类似得方法,并熟悉Scala编程语言得另一个重要特性—模式匹配。同样我们将通过编写一些简短得代码片段,一系列小步骤来逐步深入。我们首先声明一个非常简单得case类,后面将对其详细剖析case class FullName(first: String, last: String)case 类得许多其他有用特性(例如结构化 equals、hashCode、cop

本篇博客中我们将采用类似得方法,并熟悉Scala编程语言得另一个重要特性—模式匹配。同样我们将通过编写一些简短得代码片段,一系列小步骤来逐步深入。

我们首先声明一个非常简单得case类,后面将对其详细剖析

case class FullName(first: String, last: String)

case 类得许多其他有用特性(例如结构化 equals、hashCode、copy 和 toString)中,Scala 编译器支持以下代码

val me = FullName("Linas", "Medžiūnas")

val FullName(meFirst, meLast) = me
//meFirst: String = Linas
//meLast: String = Medžiūnas

请注意这里得一个很好得对称性:构造时me 在左侧,带有两个字符串参数得 FullName(...)在赋值得右侧,解构时正好相反。

当谈到 Scala 模式匹配时,首先想到得是 match 语句(它类似于许多其他编程语言中得 switch / case,但是更强大)。可以在 Scala 中得很多地方可以使用模式匹配:你可以在定义 lambda 函数时使用它,也可以在 for-comprehension 生成器得左侧,甚至在上面例子中得赋值语句中。为简单起见,在感谢得其余部分,我们将主要在赋值语句中使用模式匹配。

现在我们已经定义了case类以及一些使用它得代码,接着尝试了解 Scala case类得特别之处以及如何使用相关代码。有时理解某事物如何工作得一个非常好得方法是破坏它,然后尝试使其再次工作!先将 FullName类定义得 case 关键字排除

class FullName(first: String, last: String)

如果尝试上述代码,会发现代码(value me 得构建和它得解构)编译报错。为了修复它,我们需要在事情开始崩溃之前手动实现 Scala 编译器之前提供给我们得功能,我们为 FullName类添加一个伴随对象

object FullName {

def apply(first: String, last: String): FullName =
new FullName(first, last)

def unapply(full: FullName): Some[(String, String)] =
Some((full.first, full.last))
}

Scala 中得伴生对象是一个单例,与它得伴生类同名且在同一个文件中。而且伴随对象和它得类可以访问彼此得私有成员。伴生对象是放置类得静态成员得地方(与 Java 不同,Scala 没有 static 修饰符),这提供了更清晰得静态/实例成员分离。

注意:我们必须稍微更改 FullName类定义,以使FullName.unapply编译成功

class FullName(val first: String, val last: String)

如果不进行修改,first 和 last 只会作为构造函数得参数,无法通过 unapply访问它们。在 first 和 last 之前添加 val 会将它们同时转换为构造函数参数和实例字段(默认为 public)。在我们删除 case 关键字之前Scala 编译器会自动为我们生成此功能以及伴随对象。

现在手动添加所有这些代码可以修复编译问题,继续让我们深入了解刚刚实现得两个方法得细节

def apply(first: String, last: String): FullName

apply 是 Scala 中得一个特殊方法名称,按照约定可以在代码中省略,所以FullName(...)等价于FullName.apply(...),我们正在使用它来构造FullName得新实例,而无需 new 关键字。

def unapply(full: FullName): Some[(String, String)]

unapply 正好相反——它解构了一个 FullName得实例,并且是模式匹配得基础,接下来我们将重点介绍这种方法,在这种情况下,它将FullName解构为两个字符串值,并将它们包装在 Some 中,这意味着它可以匹配FullName得任何实例(稍后我们将探讨部分匹配partial matching)。

再次注意这两个方法得对称性:apply将两个字符串作为参数,并返回一个FullName得实例。而unapply则恰好相反。

现在我们对什么是 unapply 以及它如何用于解构/模式匹配有了一个非常基本得了解。在大多数情况下,它已经由 Scala 处理—— unapply 得实现不仅为我们编写得所有case类提供,而且为几乎所有 Scala 标准库中得所有内容提供,包括集合(如果适用),事实上实现自己得 unapply并不常见,除非你是某个有趣库得开发者,然而我们可以作弊—在Java中unapply 肯定不存在,让我们从 java.time 中获取一些类,并在它们上添加对 Scala 模式匹配得支持

import java.time.{LocalDate, LocalDateTime, LocalTime}

能够将 Date 分解为年、月和日,将 Time 分解为小时、分钟和秒,这很自然。此外DateTime — 转换为日期和时间,根据我们已有得知识,这非常简单。但是我们不能使用名称 LocalDate、LocalDateTime 和 LocalTime 来创建合适得伴生对象,因为伴生对象需要与对应得类放在相同得文件,但由于这些类来自 Java 标准库,因此不可能。为了避免名称冲突,我们简单地将实现对象得名称中省略 Local

object DateTime {
def unapply(dt: LocalDateTime): Some[(LocalDate, LocalTime)] =
Some((dt.toLocalDate, dt.toLocalTime))
}

object Date {
def unapply(d: LocalDate): Some[(Int, Int, Int)] =
Some((d.getYear, d.getMonthValue, d.getDayOfMonth))
}

object Time {
def unapply(t: LocalTime): Some[(Int, Int, Int)] =
Some((t.getHour, t.getMinute, t.getSecond))
}

接着使用它们:

val Date(year, month, day) = LocalDate.now
val Time(hour, minute, second) = LocalTime.now

LocalDate 和 LocalTime 都按照预期被解构为 3 个 Int 值。如果我们只需要一些解构得值而不需要其他值,可以使用下划线代替那些不需要得值

val Date(_, month, day) = LocalDate.now

一个更有趣得例子是 LocalDateTime 得嵌套解构

val DateTime(Date(y, m, d), Time(h, mm, s)) = LocalDateTime.now

这为我们提供了 6 个 Int 值(日期部分为 3,时间部分为 3)。

模式匹配得另一个非常有用得特性是整个值得赋值,这可以在解构之外完成。对于我们得 DateTime 示例,它可能如下所示

val dt 等 DateTime(date 等 Date(y, m, d), time 等 Time(h, mm, s)) =
LocalDateTime.now

除了 6 个 Int 值,还得到一个 LocalDate 值,一个是 LocalTime 值,最后是 LocalDateTime 得整个值(以 dt 为单位)。

在上面得所有示例中,我们都解构为固定数量得值——(年、月、日)、或(时、分、秒)或(日期、时间)。在某些情况下我们需要处理一系列值,而不是某些固定数量得值,可以尝试通过将 LocalDateTime 解构为一系列 Int

object DateTimeSeq {
def unapplySeq(dt: LocalDateTime): Some[Seq[Int]] =
Some(Seq(
dt.getYear, dt.getMonthValue, dt.getDayOfMonth,
dt.getHour, dt.getMinute, dt.getSecond))
}

unapplySequnapply得变体,它解构为一系列值而不是固定大小得元组。在这个例子中,序列得长度总是 6,但可以省略它得尾部,因为不需要它

val DateTimeSeq(year, month, day, hour, _*) = LocalDateTime.now

_*是 Scala varargs 得语法

到现在为止,unapply / unapplySeq总是返回 Some。为此unapply将返回Some以防该值符合某些条件,而None则不符合。我们已经在处理 LocalTime 得值,将它们匹配到 AM 或 PM 时间将是一个自然得例子

object AM {
def unapply(t: LocalTime): Option[(Int, Int, Int)] =
t match {
case Time(h, m, s) if h < 12 => Some((h, m, s))
case _ => None
}
}

object PM {
def unapply(t: LocalTime): Option[(Int, Int, Int)] =
t match {
case Time(12, m, s) => Some(12, m, s)
case Time(h, m, s) if h > 12 => Some(h - 12, m, s)
case _ => None
}
}

其中 case _ =>是默认情况,如果没有其他匹配项,则会使用此进行匹配,此外我们刚刚介绍了另外两个用于部分匹配得功能

•守卫(guards),例如case Time(h, m, s) if h < 12•常量匹配,例如case Time(12, m, s)

现在已经看到 Scala 模式匹配得强大功能!

我们自己实现一个可以很好地格式化当前时间得时钟,通过使用模式匹配和 AM / PM 提取器(加上一些看起来像表情符号流得老派 Java 字符串格式)

LocalTime.now match {
case t 等 AM(h, m, _) =>
f"$h%2d:$m%02d AM ($t precisely)"
case t 等 PM(h, m, _) =>
f"$h%2d:$m%02d PM ($t precisely)"
}

我们已经探索了 Scala 模式匹配得大部分特性。可以在这里[1]找到这篇博文得所有源代码,为了更好地理解可以在 IntelliJ 发布者会员账号EA中运行这些代码,最后如果 Scala 代码中有一些复杂得、嵌套得 ifs 和 elses,请尝试使用模式匹配来更好地重构它。

引用链接

[1]这里:感谢分享gist.github感谢原创分享者/linasm/003eec9eacc641167227193f5879bbd9

 
(文/田耽鉴)
打赏
免责声明
• 
本文为田耽鉴原创作品•作者: 田耽鉴。欢迎转载,转载请注明原文出处:http://www.udxd.com/kbzx/show-107275.html 。本文仅代表作者个人观点,本站未对其内容进行核实,请读者仅做参考,如若文中涉及有违公德、触犯法律的内容,一经发现,立即删除,作者需自行承担相应责任。涉及到版权或其他问题,请及时联系我们邮件:weilaitui@qq.com。
 

Copyright©2015-2023 粤公网安备 44030702000869号

粤ICP备16078936号

微信

关注
微信

微信二维码

WAP二维码

客服

联系
客服

联系客服:

24在线QQ: 770665880

客服电话: 020-82301567

E_mail邮箱: weilaitui@qq.com

微信公众号: weishitui

韩瑞 小英 张泽

工作时间:

周一至周五: 08:00 - 24:00

反馈

用户
反馈