4Scala for the Impatient(第2版)

模式匹配

模式的力量

match表达式提取器模式类型守卫

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第4章:模式匹配

一、导读

1.1 本章学习目标

第4章讲解 Scala 强大的模式匹配:match 表达式、提取器、模式类型。

本章核心学习目标:

  • 掌握 match 表达式:替代 switch
  • 理解提取器(Extractor):自定义模式
  • 掌握各种模式类型:常量、变量、类型、守卫

1.2 为什么本章如此重要

模式匹配是 Scala 最强大的特性之一。它让复杂条件逻辑变得清晰优雅。

1.3 核心问题引导

模式匹配比 if/else 有什么优势?

提取器如何工作?

什么是模式守卫?


二、核心概念详解

4.1 基本模式匹配

scala
// 常量匹配
def describe(x: Any): String = x match {
    case 0 => "zero"
    case 1 => "one"
    case "hello" => "greeting"
    case true => "truth"
    case _ => "something else"
}

// 类型匹配
def typeOf(x: Any): String = x match {
    case s: String => s"String: $s"
    case i: Int => s"Int: $i"
    case d: Double => s"Double: $d"
    case list: List[_] => s"List of ${list.size} elements"
    case _ => "unknown"
}

// 带守卫的模式
def classify(n: Int): String = n match {
    case x if x < 0 => "negative"
    case 0 => "zero"
    case x if x > 0 && x <= 10 => "small positive"
    case x if x > 10 => "large positive"
}

4.2 集合模式

scala
// 列表模式
val list = List(1, 2, 3, 4, 5)
list match {
    case Nil => "empty"
    case head :: tail => s"head: $head, tail: $tail"
    case List(1, 2, _*) => "starts with 1, 2"
    case List(_, _, x, _*) => s"third element: $x"
}

// 元组模式
val point = (3, 4)
point match {
    case (0, 0) => "origin"
    case (x, 0) => s"on x-axis at $x"
    case (0, y) => s"on y-axis at $y"
    case (x, y) => s"at ($x, $y)"
}

// case 类模式
sealed trait Shape
case class Circle(radius: Double) extends Shape
case class Rectangle(width: Double, height: Double) extends Shape

def area(shape: Shape): Double = shape match {
    case Circle(r) => math.Pi * r * r
    case Rectangle(w, h) => w * h
}

4.3 提取器

scala
// 自定义提取器
object Email {
    def unapply(str: String): Option[(String, String)] = {
        val parts = str.split("@")
        if (parts.length == 2) Some((parts(0), parts(1)))
        else None
    }
}

// 使用
"alice@example.com" match {
    case Email(user, domain) => println(s"User: $user, Domain: $domain")
    case _ => println("Not an email")
}

// 变量提取器
object Twice {
    def apply(s: String): String = s + s
    def unapply(s: String): Option[String] = {
        val length = s.length / 2
        if (length * 2 == s.length && s.substring(0, length) == s.substring(length))
            Some(s.substring(0, length))
        else None
    }
}

"abcabc" match {
    case Twice(x) => println(s"Repeated: $x")  // Repeated: abc
    case _ => println("Not repeated")
}

三、实战案例

3.1 案例一:JSON 解析器

scala
sealed trait JsonValue
case class JsonString(value: String) extends JsonValue
case class JsonNumber(value: Double) extends JsonValue
case class JsonBool(value: Boolean) extends JsonValue
case class JsonNull() extends JsonValue
case class JsonArray(values: List[JsonValue]) extends JsonValue
case class JsonObject(fields: Map[String, JsonValue]) extends JsonValue

def prettyPrint(json: JsonValue, indent: Int = 0): String = {
    val pad = "  " * indent
    json match {
        case JsonString(s) => s""""$s""""
        case JsonNumber(n) => n.toString
        case JsonBool(b) => b.toString
        case JsonNull() => "null"
        case JsonArray(values) =>
            val items = values.map(v => prettyPrint(v, indent + 1))
            s"[\n${items.map(i => pad + "  " + i).mkString(",\n")}\n$pad]"
        case JsonObject(fields) =>
            val items = fields.map { case (k, v) =>
                s""""$k": ${prettyPrint(v, indent + 1)}"""
            }
            s"{\n${items.map(i => pad + "  " + i).mkString(",\n")}\n$pad}"
    }
}

val json = JsonObject(Map(
    "name" -> JsonString("Alice"),
    "age" -> JsonNumber(30),
    "scores" -> JsonArray(List(JsonNumber(95), JsonNumber(87)))
))
println(prettyPrint(json))

四、本章小结

match 表达式是强大的条件分支工具

提取器通过 unapply 自定义匹配逻辑

sealed trait + case 类配合模式匹配实现代数数据类型


五、思考与练习

模式匹配的穷举检查如何工作?

如何实现一个正则表达式提取器?


4/7模式匹配
0%