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方法,用来测试该函数对特定的值是否可用,在调用真正函数之前会先进行测试

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

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