强大的Scala模式匹配
用过Scala的模式匹配,感觉Java的弱爆了。Scala几乎可以匹配任何数据类型,如果默认的不能满足你的要求,你可以自定义模式匹配。
介绍Scala的模式匹配前,我们先了解清楚unapply()与unapplySeq()两个方法:
名字叫做unapply和unapplySeq的方法在Scala里也是有特殊含义的。
我们前面说过case class在做pattern match时非常好用,而除case class之外,有unapply或unapplySeq方法的对象在pattern match时也有非常好的应用场景。
比方这段代码:
1 |
|
我们定义了一个unapply方法,用来计算平方根。
我们能够像调用普通方法一样的调用它:
1 |
|
这样会得到36的平方根:6。实际上返回值是Some(6)。
上面的方式是对unapply的浪费。unapply真正的优点是这种:
1 |
|
这样我们无需显式调用unapply方法,而把是它用在pattern match中。让编译器替我们调用它。
当我们写下这段pattern match的代码时,编译器事实上替我们做了好几件事:
- 调用unapply,传入number
- 接收返回值并推断返回值是None,还是Some
- 假设是Some,则将其解开,并将当中的值赋值给n(就是case Square(n)中的n)
这段代码反编译出来是这个样子的:
1 |
|
假设没有unapply方法和pattern match语法之间的这样的结合,我们自己写代码要写成什么样子呢?
也许会比上面反编译的代码简单一些,可是显式地调用开平方的方法。用if else来推断Option,以及将真正的返回值从Option里面解出来这三件事是免不掉的。
unapplySeq和unapply的作用非常是类似,比如这样:
1 |
|
我们定义一个unapplySeq方法,用逗号作为分隔符来把字符串拆开。
然后我们能够这样应用它:
1 |
|
与上面的样例非常是类似,只是编译器在这里替我们做的事情很多其它了:
- 调用unapplySeq,传入namesString
- 接收返回值并推断返回值是None,还是Some
- 假设是Some,则将其解开
- 推断解开之后得到的sequence中的元素的个数是否是三个
- 假设是三个,则把三个元素分别取出,赋值给first,second和third
假设没有unapplySeq方法和pattern match语法之间的这样的结合,我们自己写代码来做这五件事会显得非常是繁琐。
大家了解清楚unapply()与unapplySeq()两个方法,我就举个很实用的例子。
现实中往往我们有这样的一个需求:比如http请求返回体为json字符串,我们往往只需要获取json中的部分字段值。
这个需求,Java的做法是,必须知道所有json中所有字段及类型,并定义对应的JavaBean,将返回的json字符串先转换为对应的JavaBean,再通过javaBean获取想要的字段信息。
那么Scala使用模式匹配就很轻松搞定直接获取想要的字段值。如下:
先定义一个unapplySeq()实现json的模式匹配(另json字符串插值写法可参考我以前文章:Scala字符串插值 - StringContext):
package spray.json
import scala.collection.SortedMap
class JsonInterpolation(sc: StringContext) {
object json {
def apply(args: JsValue*): JsValue =
new JsonParser(ParserInput(sc, args), true).parseJsValue()
def unapplySeq(input: JsValue): Option[Seq[JsValue]] = {
val placeHolders = Seq.range(0, sc.parts.length-1).map(x => JsNumber(Integer.MAX_VALUE - x) )
val pi = ParserInput(sc, placeHolders)
val pattern = new JsonParser(pi, true).parseJsValue()
val results = collection.mutable.ArrayBuffer[JsValue]()
Seq.range(0, sc.parts.length-1).foreach { x => results += null }
try {
patternMatch(pattern, input, placeHolders, results)
Some(results.toSeq)
}
catch {
case ex: Throwable => None
}
}
// TODO report friendly
private def patternMatch(pattern: JsValue, input: JsValue, placeHolders: Seq[JsValue], results: collection.mutable.ArrayBuffer[JsValue]): Unit = {
def isPlaceHolder(value: JsNumber) = {
val num = value.value.toInt
val index = Integer.MAX_VALUE - num.toInt
num > 0 && index < placeHolders.size && placeHolders(index).eq(value)
}
pattern match {
case x: JsObject =>
x.fields.foreach {
case (key, n @ JsNumber(num)) if isPlaceHolder(n) =>
val index = Integer.MAX_VALUE - num.toInt
assert(input.asJsObject.fields contains key)
results(index) = input.asJsObject.fields(key)
case (key, value) =>
assert(input.asJsObject.fields contains key)
patternMatch(value, input.asJsObject.fields(key), placeHolders, results)
}
case x: JsArray =>
assert(input.isInstanceOf[JsArray])
assert(input.asInstanceOf[JsArray].elements.size >= x.elements.size)
x.elements.zipWithIndex.foreach {
case (x: JsNumber, y: Int) if isPlaceHolder(x) =>
val index = Integer.MAX_VALUE - x.value.toInt
results(index) = input.asInstanceOf[JsArray].elements(y)
case (x: JsValue,y: Int)=>
patternMatch(x, input.asInstanceOf[JsArray].elements.apply(y), placeHolders, results)
}
case x: JsString =>
assert(x == input)
case x: JsBoolean =>
assert(x == input)
case x: JsNumber =>
assert(x == input)
case x@ JsNull =>
assert(x == input)
}
}
}
}
定义好unpplySeq(),那么下面就看下如何通过模式匹配获取json字符传中我们关系的字段值:
import spray.json._ /**
* 类功能描述:
*
* @author WangXueXing create at 19-5-11 下午10:37
* @version 1.0.0
*/
object ObjectMatch {
def main(args: Array[String]): Unit = {
val json = json"""{"id": 12333,"sku_no":"sku35352523"}"""
json match {
case json"""{"sku_no": $skuNo}""" => println(skuNo)
case _ => println(json)
} }
}
输出:
"sku35352523"
如上,我们就可以选择性的拿到sku_no字段的值,是不是轻松搞定!
强大的Scala模式匹配的更多相关文章
- scala模式匹配详细解析
一.scala模式匹配(pattern matching) pattern matching可以说是scala中十分强大的一个语言特性,当然这不是scala独有的,但这不妨碍它成为scala的语言的一 ...
- scala模式匹配的使用
Scala模式匹配 Tip1:模式总是从上往下匹配,如果匹配不到则匹配case_项(类似Java中的default) Tip2:与Java和C语言不同,不需要在每个分支末尾使用break语句退出(不会 ...
- Spark记录-Scala模式匹配
Scala模式匹配 模式匹配是Scala函数值和闭包后第二大应用功能.Scala为模式匹配提供了极大的支持,处理消息. 模式匹配包括一系列备选项,每个替代项以关键字大小写为单位.每个替代方案包括一个模 ...
- Scala模式匹配和类型系统
1.模式匹配比java中的switch case强大很多,除了值,类型,集合等进行匹配,最常见的Case class进行匹配,Master.scala有大量的模式匹配. Case "_&qu ...
- scala模式匹配与样例类
样本类:添加了case的类便是样本类.这种修饰符可以让Scala编译器自动为这个类添加一些语法上的便捷设定.如下: 1.添加与类名一致的工厂方法.也就是说,可以写成Var("x") ...
- Scala模式匹配| 隐式转换
1. 模式匹配 Scala中的模式匹配类似于Java中的switch语法,但是更加强大.模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分 ...
- Scala模式匹配和样例类
Scala有一个十分强大的模式匹配机制,可以应用到很多场合:如switch语句.类型检查等.并且Scala还提供了样例类,对模式匹配进行了优化,可以快速进行匹配. 1.字符匹配 def mai ...
- 10、scala模式匹配
一.模式匹配1 1.介绍 模式匹配是Scala中非常有特色,非常强大的一种功能.模式匹配,其实类似于Java中的swich case语法,即对一个值进行条件判断,然后针对不同的条件, 进行不同的处理. ...
- 12. Scala模式匹配
12.1 match 12.1.1 基本介绍 Scala中的模式匹配类似于Java中的switch语法,但是更加强大 模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需 ...
随机推荐
- 数据持久化之bind Mounting
一.默认情况 1.创建一个Nginx测试镜像 Dockerfile: FROM nginx:latest WORKDIR /usr/share/nginx/html COPY index.html i ...
- Docker Hub镜像加速器
国内从 Docker Hub 拉取镜像有时会遇到困难,此时可以配置镜像加速器.Docker 官方和国内很多云服务商都提供了国内加速器服务. 1.配置加速地址 vim /etc/docker/daemo ...
- Elasticsearch 使用 php curl 插入数据
<?php /** * Created by PhpStorm. * User: func7 * Date: 2018/11/8 * Time: 11:24 */ set_time_limit( ...
- ASP.NET Core appsettings.json 文件
ASP.NET Core appsettings.json 文件 在本节中,我们将讨论 ASP.NET Core 项目中appsettings.json文件的重要性. 在以前的 ASP.NET 版本中 ...
- Python - 常规操作Excel - 第二十六天
前言 作为一名资深程序员,通过代码熟练操作Excel是必不可少的技能,本章主要讲解Python通过openpyxl第三方库(官方文件说明)对Excel进行操作,使Excel程序化操作更为简单快捷. o ...
- Python - MySQL - mysql-connector -第二十四天
序言 搭建MySQL环境MySQL Server. 请参考:https://blog.csdn.net/m0_37788308/article/details/79965378 或者 https:// ...
- Comet OJ-2019国庆欢乐赛
国庆玩的有点嗨,开学了补题. A轰炸平面镇魂曲 题目描述 虹村万泰是一位二维世界的替身使者,他的替身 "轰炸平面镇魂曲" 能产生一条直线分割整个平面. 一开始,平面上有一个矩形,其 ...
- CSS3动画实践——简易牛顿摆
最近在练习CSS3的关键帧动画(keyframes),于是做了一个简单的牛顿摆(听名字可能陌生,但你一定见过它): 先上代码(老版本IE可能存在兼容性问题): <!DOCTYPE html> ...
- 项目进程中input的onblur事件挂不上去,失效问题解决记录
一开始直接在js文件中写 var n=document.querySelector("input"); n.onblur=funcition(){ ///事件过程吧啦啦啦 }; 事 ...
- 服务器Oracle数据库配置与客户端访问数据库的一系列必要设置
tips:所有路径请对应好自己电脑的具体文件路径. 一.服务器及Oracle数据库设置 1.刚装完的Oracle数据库中只有一个dba账户,首先需要创建一个用户. 2.配置监听,C:\app\Admi ...