强大的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关键字进行声明,当需 ...
随机推荐
- 怎样用 C# 快速比较 2 个文件是否是相同的文件?
方案1: 直接贴代码了: using System; using System.Collections.Generic; using System.IO; using System.Linq; usi ...
- kali渗透综合靶机(十六)--evilscience靶机
kali渗透综合靶机(十六)--evilscience靶机 一.主机发现 1.netdiscover -i eth0 -r 192.168.10.0/24 二.端口扫描 1. masscan --ra ...
- Scrapy 运行多个爬虫
本文所使用的 Scrapy 版本:Scrapy==1.8.0 一个 Scrapy 项目下可能会有多个爬虫,本文陈述两种情况: 多个爬虫 所有爬虫 显然,这两种情况并不一定是等同的.假设当前项目下有 3 ...
- Linux 配置程序包源 Nuget
编辑文件NuGet.Config vi ~/.nuget/NuGet/NuGet.Config 新增源 <add key="fz" value="http://19 ...
- 打造游戏金融小程序行业测试标准腾讯WeTest携各专家共探品质未来
在获客成本不断上升的时代里,产品品质愈发是互联网应用的决胜标准.随着用户需求更加多样,开发者不仅要深挖应用功能,更需要面向业务所在领域,建立全面.专业的测试架构,掌控开发进度.提高开发效率,才能在互联 ...
- js-深拷贝浅拷贝
深拷贝浅拷贝可以考察一个人的很多方面,例如:基本功,逻辑能力,编码能力: 在实际工作中的应用:比如用于页面展示的数据状态,与需要传给后端的数据包中,有部分字段的值不一致的话,就需要在传参时根据接口文档 ...
- js中对象字面量
一.对象字面量语法 var person={ name:'小王', age:18, _pri:233 } 成员名称的单引号不是必须的 最后一个成员结尾不要用逗号,不然在某些浏览器中会抛出错误 成员名相 ...
- 解决使用elementUI框架el-upload跨域上传时session丢失问题
解决方法一: 1.使用elementUI框架el-upload跨域上传时,后端获取不到cookie,后端接口显示未登录,在添加了 with-credentials="true"后依 ...
- bat弹出确认或取消窗口
需要在bat脚本里面弹出取消/确认框提示,可以用下面的案例: 示例: @echo off setlocal enabledelayedexpansion set Vbscript=Msgbox(&qu ...
- Mysql时区无法识别
Unable to connect to database. Tried 1 times {:error_message=>“Java::JavaSql::SQLException: The s ...