Scala模式匹配和样例类

  • match表达式是一个更好的switch,不会有意外调入下一个分支的问题。其他语言需要使用break来避免。
  • 如果没有模式能够匹配,会抛出MatchError,可以用case _模式来避免。
  • 模式可以包含一个随意定义的条件,叫做守卫。
  • 你可以对表达式的类型进行匹配。优先选择模式匹配而不是isInstanceOf。
  • 你可以匹配数组,元组,样例类的模式,然后将匹配到的不同部分帮顶到变量。
  • 在for表达式中,不能匹配的情况会被安静的跳过。
  • 样例类是编译器会为之自动产出模式匹配所需要的方法的类。
  • 用Option来存放对于可能存在也可能不存在的值,这比null更安全。

更好的switch

var sign = ch match {
case '+' => 1
case '-' => -1
case _ if Character.isDigit(ch) => digit = Character.digit(ch)
//添加守卫,守卫可以是任何Boolean条件
case _ => 0
}

模式中的变量

如果case后面跟着一个变量名,那么匹配的表达式会被赋值给那个变量。你当然可以在守卫中使用变量:

str(i) match {
case '+' => sign = 1
case '-' => sign = -1
case ch if Character.isDigit(ch) => digit = Character.digit(ch)}

scala可以识别你匹配的是变量还是常量:

import scala.math._
x match{
case Pi => ...
//scala不会把Pi当成一个变量。
...
}

类型匹配

obj match {
case x : Int => x
//匹配的表达式会被绑定到变量x
case s : String => Integer.parseInt(s)
case _ : BigInt => Int.MaxValue
case _ : => 0
}

匹配数组,列表和元组

利用模式匹配可以很轻松的访问复杂结构的各组成部分,这种操作被称作“析构”。

arr match{
case Array(0) => "0"
//匹配包含0的数组
case Array(x,y) => x + " " + y
//匹配两个元素的数组,并把数组元素分别赋值给x,y
case Array(0,_*) => "0..."
//匹配以0开始的数组
case _ => "something else"}

lst match { 
case 0 :: Nil => "0"
case x :: y :: Nil => x + " " + y
case 0 :: tail => "0..."
case _ => "something else"}

pair match {
case (0,_) => "0..."
case (y, 0) => y + " 0"
case _ => "neither is 0"}

提取器

上面的模式匹配数组,列表和元组,这些操作的背后是提取器机制----带有从对象中提取值的unapply,unapplySeq方法的对象。unapply用于提取固定数量的对象,unapplySeq提取的是一个序列。

arr match {
case Array(0,x) => ...
...
}

Array伴生对象是一个提取器,它定义了unapplySeq方法,Array.unapplySeq(arr)产出一个序列的值,第一个值与0进行比较,第二个值赋值给x。

val pattern = "([0-9]+) ([a-z]+)".r
"99 bottles" match{
case pattern(num,item) => ...
//将num设为99,item设为"bottles"}

变量声明中的模式

val (x , y ) = (1,2)
val Array(first , second , _*) =  arr

for表达式中的模式

for((k,v) <- System.getProperties())
    println(k + "->"+v)
for((k,"") <- System.getProperties())
    println(k)
//失败的匹配会被安静的忽略,这条语句只会打印出值为空白的键

样例类

样例类是一种特殊的类,它们经过优化以被用于模式匹配。 当你声明样例类时,下面几件事情会自动发生:

  • 构造器中的每个参数都成为val
  • 在伴生对象中提供apply方法,让你不用new关键字就能构造出相应的对象。
  • 提供unapply方法让模式匹配可以工作。
object CaseClassDemo extends App{
    def m(p : Person){
        p match {
        case Teacher(_) => println("This is a teacher")
        case Teacher("Peter") => println("This is teacher Peter")
        case Student(_) => println("This is a student")
        case _ => println("This is unknown")
      }
    }

    m(Student("A"))
    //"This is a student"
    m(Teacher("A"))
    //"This is a teacher"
    m(Teacher("Peter"))
    //"This is a teacher Peter"
}
abstract class Person
case class Teacher(name : String) extends Person
case class Student(name : String) extends Person

模拟枚举

样例类可以让你在Scala中模拟出枚举类型

sealed abstract class TrafficLightColor
//将TrafficLightColor声明为sealed(密封类)可以让编译器为我们检查match语句是否列出了所有的情况。
case object Red extends TrafficLightColor
case object Yellow extends TrafficLightColor
case object Green extends TrafficLightColor

color match {
    case Red => "stop"
    case Yellow => "hurry up"
    case Green => "go"
    }

Option类型

标准库中的Option类型用样例类来表示那些可能存在,也可能不存在的值。 Option支持泛型,Some("Fred")的类型为Option[String]

scores.get("Alice") match {
case Some(score) => println(score)
case None => println("No score")}

scores.getOrElse("Alice","No score")

Comments !