Essential Scala: Pattern Matching

Standard patterns

Literal patterns: 字面值模式

字面值模式匹配一个特殊的值,Scala中除了primitive值,Strings,nulls和() 都可以使用:

(1 + 1) match {
    case 1 => "It's one!" 
    case 2 => "It's two!" 
    case 3 => "It's three!"
}
// res: String = It's two!
Person("Dave", "Gurnell") match {
    case Person("Noel", "Welsh") => "It's Noel!" 
    case Person("Dave", "Gurnell") => "It's Dave!"
}
// res: String = It's Dave!
println("Hi!") match { 
    case () => "It's unit!"
}
// Hi!
// res: String = It's unit!

Constant patterns: 常量模式

标示符以大写字母开始的叫做常量,用以匹配一个预定义的常量:

val X = "Foo"
// X: String = Foo
val Y = "Bar"
// Y: String = Bar
val Z = "Baz"
// Z: String = Baz
"Bar" match {
    case X => "It's foo!" 
    case Y => "It's bar!" 
    case Z => "It's baz!"
}
// res: String = It's bar!

Alternative patterns: 选择模式

“|” 竖线用于选择模式:

"Bar" match {
    case X | Y => "It's foo or bar!"  // X 或 Y
    case Z => "It's baz!"
}
// res: String = It's baz!

Variable capture: 变量捕获

小写字母开都的标识符用于绑定值,绑定后该变量可以在 => 右侧使用:

Person("Dave", "Gurnell") match { 
    case Person(f, n) => f + " " + n
}
// res: String = "Dave Gurnell"

@ 操作符,写作 x @ y,支持我们将值捕获到x中,同时有满足模式y,x必须是一个变量模式(即标示符小写),y可以是任何模式:

Person("Dave", "Gurnell") match {
    case p @ Person(_, s) => s"The person $p has the surname $s"
}
// res: String = "The person Person(Dave,Gurnell) is called Dave Gurnell"

即将 @ 后面的整个 Person(, s) 起了个别名p,同时 Person(, s)作为匹配的模式.

Wildcard patterns: 通配符模式

符号 “_” 用于匹配任何值并忽略,在两种场景中比较有用:一种是在别的模式中进行嵌套,另一种是用于匹配任何剩余的分支:

Person("Dave", "Gurnell") match {
    case Person("Noel", _) => "It's Noel!" 
    case Person("Dave", _) => "It's Dave!"
}
// res: String = It's Dave!
Person("Dave", "Gurnell") match {
    case Person(name, _) => s"It's $name!"
}
// res: String = It's Dave!
Person("John", "Doe") match {
    case Person("Noel", _) => "It's Noel!" 
    case Person("Dave", _) => "It's Dave!" 
    case _ => "It's someone else!"
}
// res: String = It's someone else!

Type patterns: 类型模式

类型模式写作 x: Y ,Y是一个类型,x是一个通配符模式或变量模式,该模式将匹配任何符合类型的值并将值绑定到x:

val shape: Shape = Rectangle(1, 2) 
// shape: Shape = Rectangle(1.0,2.0)
shape match {
    case c : Circle => s"It's a circle: $c!" 
    case r : Rectangle => s"It's a rectangle: $r!" 
    case s : Square => s"It's a square: $s!"
}
// res: String = It's a rectangle: Rectangle(1.0,2.0)!

Tuple patterns: 元组模式

(1, 2) match {
    case (a, b) => a + b
}
// res: Int = 3

Guard expressions: 守卫表达式

123 match {
    case a if a % 2 == 0 => "even" 
    case _ => "odd"
}
// res: String = odd

Custom Patterns

Extractors

Case class extractors,case类自身伴生对象中自带的解析器:

Person("Dave", "Gurnell") match { 
    case Person(f, l) => List(f, l)
}
// res: List[String] = List(Dave, Gurnell)

Regular expressions:

import scala.util.matching.Regex
val r = new Regex("""(\d+)\.(\d+)\.(\d+)\.(\d+)""")
// r: scala.util.matching.Regex = (\d+)\.(\d+)\.(\d+)\.(\d+)
"192.168.0.1" match {
    case r(a, b, c, d) => List(a, b, c, d)
}
// res: List[String] = List(192, 168, 0, 1)

Lists and Sequences:

List(1, 2, 3) match {
    case List(a, b, c) => a + b + c
}
// res: Int = 6

Nil匹配一个空列表:

Nil match {
    case List(a) => "length 1" 
    case Nil => "length 0"
}
// res: String = length 0

单例对象 :: 用于匹配列表的head和tail:

List(1, 2, 3) match {
    case ::(head, tail) => s"head $head tail $tail" 
    case Nil => "empty"
}
// res: String = head 1 tail List(2, 3)

或者:

List(1, 2, 3) match {
    case head :: tail => s"head $head tail $tail" 
    case Nil => "empty"
}
// res: String = head 1 tail List(2, 3)

组合使用::, Nil 和 _ 可以用于匹配任意长度的列表:

List(1, 2, 3) match {
    case Nil => "length 0"
    case a :: Nil => s"length 1 starting $a"
    case a :: b :: Nil => s"length 2 starting $a $b"
    case a :: b :: c :: _ => s"length 3+ starting $a $b $c"
}
// res: String = length 3+ starting 1 2 3

自定义一个解析器:

object Email {
    def unapply(str: String): Option[(String, String)] = {
        val parts = str.split("@")
        if (parts.length == 2) Some((parts(0), parts(1))) else None 
    }
}
"dave@underscore.io" match {
    case Email(user, domain) => List(user, domain)
}
// res: List[String] = List(dave, underscore.io)
"dave" match {
    case Email(user, domain) => List(user, domain) case _ => Nil
}
// res: List[String] = List()

类似的用法用于匹配任何字符串并转换为大写:

object Uppercase {
    def unapply(str: String): Option[String] = Some(str.toUpperCase)
}
Person("Dave", "Gurnell") match {
    case Person(f, Uppercase(l)) => s"$f $l"
}
// res: String = Dave GURNELL

Wildcard sequence patterns: 通配符序列模式

List(1, 2, 3, 4, 5) match { 
    case List(a, b, _*) => a + b
}
// res: Int = 3
"the quick brown fox" match { 
    case Words(a, b, _*) => a + b
}
// res: String = "thequick"

将剩余元素绑定到一个变量:

"the quick brown fox" match {
    case Words(a, b, rest @ _*) => rest
}
// res: Seq[String] = WrappedArray("brown", "fox")