Scala 模式匹配

样本类 case class

类前加上case修饰符,编译器会添加额外的语法,更好的支持模式匹配

  • 添加与类名称相同的工厂方法
  • 样本类参数列表中的所有参数,自动添加前缀val
  • 添加toString, hashCode,equals方法
  • 添加copy方法,方便进行复制,同时修改部分参数(默认参数和命名形参结合)
//定义一个表达式和一元运算
abstract class Expr
case class UnOp(operator: String, arg: Expr) extends Expr

反编译Expr.class,UnOp.class和UnOp$.class,查看具体添加的方法


iExpr.class

UnOp.class
UnOp$.class

模式匹配 Pattern Match

Scala中的匹配表达式(match expression)类似于Java的switch,不过匹配一个之后就会返回(等同于隐式添加了break),如果都没有匹配到抛出MatchError异常
不过Scala先写选择表达式

//Scala
selector match { alternatives }
//Java
switch (selector) { alternatives }

模式匹配包含一序列的选项,开头是case,每个选项包含一个模式,中间是箭头分隔,右侧是一个或多个表达式,按代码的先后顺序比较每个模式

case pattern => expressions

模式的种类

通配符模式 wildcard pattern
_匹配所有值,但没有变量引用匹配到的值,用来忽略对象中不关心的部分

expr match {
   case BinOp(_, _, _) => println(expr + " is a binary operation")
    case _ => // 处理默认情况,返回值为unit值的()
}

常量模式 constant pattern
只匹配自身,任何字面量都可以做常量,val和单例对象也可以做常量

变量模式 variable pattern
类似于通配符匹配,匹配任何对象,在case语言的右侧,变量可以引用匹配到的值
区分:以小写字母开始的名称被当做是变量模式,否则当做常量
可以使用反引号包住变量名称,它将会被理解为常量

val v42 = 42
Some(3) match {
  case Some(`v42`) => println("42") //不用反引号,理解为变量,一直输出42
  case _ => println("Not 42") //输出
}

构造器模式 constructor pattern
构造器的参数也可以是模式(深度匹配)

case BinOp("+", e, Number(0)) => println("a deep match") //三层匹配

序列模式 Sequence pattern
匹配序列类型List,Array等,_*作为模式的最后一个元素,匹配任意数量的元素

case List(0, _, _) => println("found it")//匹配从零开始的,有三个元素的列表
case List(0, _*) => println("found it")//无论任意长度,以零开头的列表
case _ =>

元组模式 Tuple pattern

case (a, b, c) => println("matched " + a + b + c)

类型模式 Type pattern
用于类型测试和类型转换

def generalSize(x: Any) = x match {
  case s: String => s.length
  case m: Map[_, _] => m.size//类型模式中的下划线是通配符
  case _ => -1
}

Scala中的类型测试和转换故意很冗长,不推荐使用

if (x.isInstanceOf[String]) {
  val s = x.asInstanceOf[String]
  s.length
} else ...

由于类型擦除,不能指定泛型的类型进行匹配

case m: Map[Int, Int] => true //无论什么类型都是true

变量绑定 Variable binding
除了变量模式之外,还可以将变量添加到任何其他模式
变量名称 @ 模式, 变量绑定模式,如果模式成功,该变量设置为匹配的对象

case UnOp("abs", e @ UnOp("abs", _)) => e

模式守卫 pattern guard

模式守卫接在模式之后,以if开头,之后任意的布尔表达式,为真时,匹配才会成功

case BinOp("+", x, y) if x == y =>
     BinOp("*", x, Number(2))

密封类 sealed class

目的是让Scala编译器来检测匹配表达式中缺失的模式组合

通过密封样本类的超类,密封的类不能添加任何新的子类,除了同一个文件中的子类。这样编译器可以确认子类,会通过警告信息表明缺少的模式组合

sealed abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
case class BinOp(operator: String, left: Expr, right: Expr) extends Expr
//产生警告
def describe(e: Expr): String = e match {
  case Number(_) => "a number"
  case Var(_) => "a variable"
}

//通过注解抑制警告
def describe(e: Expr): String = (e: @unchecked) match {
  case Number(_) => "a number"
  case Var(_) => "a variable"
}

Option类型

Scala定义了一个名为Option的标准类型用于表示可选值。这样的值可以有两种形式:Some(x),其中x是实际值,或者是None对象,表示缺失的值。
Scala集合中的一些标准操作会产生可选值,例如Scala的Map的get方法会生成Some(x)或返回None。

def show(x: Option[String]) = x match {
  case Some(s) => s
  case None => "?"
}

使用PartialFunction

花括号中的case序列可以当做广义的函数字面量,正常的函数字面量只有一个入口点和参数列表,case序列可以有多个入口点,每个case语句就是一个入口点,对应有自己的参数列表

val withDefault: Option[Int] => Int = {
    case Some(x) => x
    case None => 0
}

该例子中有两个case,对应两个入口点

case序列写作偏函数,可以避免匹配错误的情况

val second: PartialFunction[List[Int],Int] = {
   case x :: y :: _ => y
}

以上函数会被翻译为

new PartialFunction[List[Int], Int] {
  def apply(xs: List[Int]) = xs match {
    case x :: y :: _ => y
  }
  def isDefinedAt(xs: List[Int]) = xs match {
    case x :: y :: _ => true
    case _ => false
  }
}

偏函数中的isDefinedAt方法,用来测试该函数对特定的值是否可用,在调用真正函数之前会先进行测试

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容

  • Scala与Java的关系 Scala与Java的关系是非常紧密的!! 因为Scala是基于Java虚拟机,也就是...
    灯火gg阅读 3,437评论 1 24
  • 模式匹配 要理解模式匹配(pattern-matching),先把这两个单词拆开,先理解什么是模式(pattern...
    JasonDing阅读 1,371评论 0 1
  • 1.scala是匹配和java中的switch的区别: ...
    Coderlxl阅读 460评论 0 0
  • 模式匹配是Scala中非常有特色且强大的功能。模式匹配类似于Java中的swich case语法即对一个值进行条件...
    SunnyMore阅读 366评论 0 1
  • 《开学第一课》已经开播十年了,今年的主题是“传统文化”。主持人化身为老师,班主任是董卿,还有撒贝宁老师。 ...
    三峡姑娘阅读 236评论 0 1