美文网首页
Scala 模式匹配

Scala 模式匹配

作者: wangdy12 | 来源:发表于2018-03-12 17:13 被阅读0次

    样本类 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 模式匹配

          本文链接:https://www.haomeiwen.com/subject/ngkryxtx.html