Skip to content

模式匹配 (Match)

Valkyrie 提供了强大的模式匹配功能,支持多种匹配模式和语法形式。

基本 Match 语法

标准 Match 语句

valkyrie
# 基本模式匹配
match value {
    case 1: "one"
    case 2: "two"
    case 3: "three"
    else: "other"
}

# 范围匹配
match score {
    case 90..=100: "A"
    case 80..=89: "B"
    case 70..=79: "C"
    case 60..=69: "D"
    else: "F"
}

# 多值匹配
match day {
    case "Saturday" | "Sunday": "Weekend"
    case "Monday"..="Friday": "Weekday"
    else: "Invalid day"
}

表达式 Match 语法

valkyrie
# 表达式形式的 match
let result = value.match {
    case 1: "one"
    case 2: "two"
    case 3: "three"
    else: "other"
}

# 链式调用
let processed = input
    .transform()
    .match {
        case Fine { value }: value * 2
        case Fail { error }: 0
    }

解构匹配

元组解构

valkyrie
match point {
    case (0, 0): "Origin"
    case (x, 0): "On X-axis at ${x}"
    case (0, y): "On Y-axis at ${y}"
    case (x, y): "Point at (${x}, ${y})"
}

# 嵌套元组
match nested {
    case ((a, b), c): "Nested: ${a}, ${b}, ${c}"
    case (x, (y, z)): "Other nested: ${x}, ${y}, ${z}"
    else: "No match"
}

数组解构

valkyrie
match array {
    case []: "Empty array"
    case [x]: "Single element: ${x}"
    case [first, second]: "Two elements: ${first}, ${second}"
    case [head, ..tail]: "Head: ${head}, Tail length: ${tail.len()}"
    case [.., last]: "Last element: ${last}"
    case [first, .., last]: "First: ${first}, Last: ${last}"
}

# 固定长度匹配
match coordinates {
    case [x, y]: "2D point: (${x}, ${y})"
    case [x, y, z]: "3D point: (${x}, ${y}, ${z})"
    else: "Unsupported dimension"
}

对象解构

valkyrie
match person {
    case { name: "Alice", age }: "Alice is ${age} years old"
    case { name, age: 18..=65 }: "${name} is working age"
    case { name, age, city: "Beijing" }: "${name} from Beijing, age ${age}"
    case { name, ..rest }: "Person ${name} with other fields"
    else: "Unknown person"
}

# 嵌套对象匹配
match user {
    case { profile: { name, email }, active: true }: 
        "Active user: ${name} (${email})"
    case { profile: { name }, active: false }: 
        "Inactive user: ${name}"
    else: "Invalid user"
}

联合类型匹配

Result 类型匹配

valkyrie
match operation_result {
    case Fine { value }: "Success: ${value}"
    case Fail { error }: "Error: ${error}"
}

# 嵌套 Result 匹配
match nested_result {
    case Fine { value: Fine { value } }: "Double success: ${value}"
    case Fine { value: Fail { error: inner_error } }: "Inner error: ${inner_error}"
    case Fail { error: outer_error }: "Outer error: ${outer_error}"
}

Option 类型匹配

valkyrie
match maybe_value {
    case Some(value): "Found: ${value}"
    case None: "Nothing found"
}

# 复杂 Option 匹配
match complex_option {
    case Some({ name, age }) if age >= 18: 
        "Adult: ${name}"
    case Some({ name, age }): 
        "Minor: ${name}"
    case None: "No person"
}

自定义联合类型匹配

valkyrie
union JsonValue {
    Null,
    Bool(bool),
    Number(f64),
    String(String),
    Array([JsonValue]),
    Object({String: JsonValue})
}

match json_value {
    case Null: "null"
    case Bool(true): "true"
    case Bool(false): "false"
    case Number(n): "number: ${n}"
    case String(s): "string: ${s}"
    case Array(items): "array with ${items.len()} items"
    case Object(map): "object with ${map.len()} keys"
}

守卫条件

基本守卫

valkyrie
match number {
    case x if x > 0: "positive"
    case x if x < 0: "negative"
    case 0: "zero"
    else: "not a number"
}

# 复杂守卫条件
match person {
    case { age, name } if age >= 65: "Senior: ${name}"
    case { age, name } if age >= 18: "Adult: ${name}"
    case { age, name } if age >= 13: "Teenager: ${name}"
    case { name, .. }: "Child: ${name}"
}

多重守卫

valkyrie
match user {
    case { role: "admin", active: true } if user.permissions.contains("write"): 
        "Full access"
    case { role: "user", active: true } if user.last_login.is_recent(): 
        "User access"
    case { active: false }: "Account disabled"
    else: "No access"
}

Extractor 模式和 Unapply

Extractor 模式基础

Extractor 模式允许在模式匹配中使用自定义的解构逻辑,通过 unapply 方法实现。这使得可以使用类似构造函数的语法进行模式匹配。

valkyrie
# 定义带有 unapply 方法的类
class User {
    name: String
    email: String
    age: i32
    
    new(name: String, email: String, age: i32) {
        self.name = name
        self.email = email
        self.age = age
    }
    
    # unapply 方法用于模式匹配中的解构
    unapply(user: User) -> Option<(String, String, i32)> {
        Some((user.name, user.email, user.age))
    }
}

# 使用 extractor 模式进行匹配
match user_data {
    case User(name, email, age) if age >= 18: 
        "Adult user: ${name} (${email})"
    case User(name, _, age): 
        "Minor user: ${name}, age ${age}"
    else: "Invalid user data"
}

复杂 Extractor 模式

valkyrie
# 定义复杂的 extractor
class EmailAddress {
    local: String
    domain: String
    
    new(email: String) {
        let parts = email.split("@")
        if parts.len() == 2 {
            self.local = parts[0]
            self.domain = parts[1]
        } else {
            panic("Invalid email format")
        }
    }
    
    # 支持多种解构方式
    unapply(email: EmailAddress) -> Option<(String, String)> {
        Some((email.local, email.domain))
    }
    
    # 可以定义多个 unapply 重载
    unapply_full(email: EmailAddress) -> Option<String> {
        Some("${email.local}@${email.domain}")
    }
}

# 使用不同的 extractor 模式
match email {
    case EmailAddress(local, "gmail.com"): "Gmail user: ${local}"
    case EmailAddress(local, "outlook.com"): "Outlook user: ${local}"
    case EmailAddress(local, domain): "User ${local} from ${domain}"
    else: "Invalid email"
}

嵌套 Extractor 模式

valkyrie
class Address {
    street: String
    city: String
    country: String
    
    unapply(addr: Address) -> Option<(String, String, String)> {
        Some((addr.street, addr.city, addr.country))
    }
}

class Person {
    name: String
    email: EmailAddress
    address: Address
    
    unapply(person: Person) -> Option<(String, EmailAddress, Address)> {
        Some((person.name, person.email, person.address))
    }
}

# 嵌套使用 extractor 模式
match person {
    case Person(name, EmailAddress(_, "gmail.com"), Address(_, "Beijing", "China")): 
        "Beijing Gmail user: ${name}"
    case Person(name, EmailAddress(local, domain), Address(_, city, _)): 
        "User ${name} (${local}@${domain}) from ${city}"
    else: "Unknown person"
}

条件 Extractor 模式

valkyrie
class Range {
    start: i32
    end: i32
    
    # 条件性的 unapply
    unapply(value: i32, range: Range) -> Option<i32> {
        if value >= range.start && value <= range.end {
            Some(value)
        } else {
            None
        }
    }
}

# 使用条件 extractor
let valid_age = Range { start: 0, end: 120 }
let adult_age = Range { start: 18, end: 120 }

match age {
    case adult_age(a): "Adult age: ${a}"
    case valid_age(a): "Valid age: ${a}"
    else: "Invalid age"
}

自定义集合 Extractor

valkyrie
class NonEmpty<T> {
    items: [T]
    
    new(items: [T]) {
        if items.is_empty() {
            panic("Cannot create NonEmpty with empty array")
        }
        self.items = items
    }
    
    unapply<T>(list: [T]) -> Option<(T, [T])> {
        if list.is_empty() {
            None
        } else {
            Some((list[0], list[1..]))
        }
    }
}

# 使用集合 extractor
match numbers {
    case NonEmpty(head, tail): "Head: ${head}, Tail length: ${tail.len()}"
    case []: "Empty list"
}

正则表达式 Extractor

valkyrie
class Regex {
    pattern: String
    
    new(pattern: String) {
        self.pattern = pattern
    }
    
    unapply(text: String, regex: Regex) -> Option<[String]> {
        # 假设有正则表达式匹配功能
        regex.find_matches(text)
    }
}

# 使用正则表达式 extractor
let email_pattern = Regex::new(r"([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})")
let phone_pattern = Regex::new(r"(\d{3})-(\d{3})-(\d{4})")

match input {
    case email_pattern([_, local, domain]): "Email: ${local}@${domain}"
    case phone_pattern([_, area, exchange, number]): "Phone: (${area}) ${exchange}-${number}"
    else: "Unknown format"
}

Extractor 最佳实践

valkyrie
# 1. 保持 unapply 方法简单和高效
class Point {
    x: f64
    y: f64
    
    # 简单直接的 unapply
    unapply(point: Point) -> Option<(f64, f64)> {
        Some((point.x, point.y))
    }
    
    # 可以提供多个 unapply 变体
    unapply_polar(point: Point) -> Option<(f64, f64)> {
        let r = (point.x * point.x + point.y * point.y).sqrt()
        let theta = point.y.atan2(point.x)
        Some((r, theta))
    }
}

# 2. 使用有意义的变量名
match coordinate {
    case Point(x, y) if x == 0.0 && y == 0.0: "Origin"
    case Point(x, 0.0): "On X-axis at ${x}"
    case Point(0.0, y): "On Y-axis at ${y}"
    case Point(x, y): "Point at (${x}, ${y})"
}

# 3. 组合使用 extractor 和其他模式
match data {
    case [Point(0.0, 0.0), ..rest]: "Starts with origin, ${rest.len()} more points"
    case [Point(x, y), Point(x2, y2)] if x == x2: "Vertical line"
    case [Point(x, y), Point(x2, y2)] if y == y2: "Horizontal line"
    case points: "General path with ${points.len()} points"
}

变量绑定

@ 绑定语法

valkyrie
match value {
    case x @ 1..=10: "Small number: ${x}"
    case x @ 11..=100: "Medium number: ${x}"
    case large @ 101..: "Large number: ${large}"
    else: "Out of range"
}

# 复杂绑定
match data {
    case person @ { name, age } if age >= 18: 
        "Adult person: ${person}"
    case child @ { name, age }: 
        "Child: ${child}"
}

嵌套绑定

valkyrie
match nested_structure {
    case { outer: inner @ { value, .. }, .. } if value > 0: 
        "Positive inner value: ${inner}"
    case { outer: inner @ { value, .. }, .. }: 
        "Non-positive inner value: ${inner}"
    else: "No match"
}

穷尽性检查

编译时检查

valkyrie
# 编译器会检查是否覆盖所有情况
match boolean_value {
    case true: "yes"
    case false: "no"
    # 不需要 else,因为已经穷尽
}

# 联合类型的穷尽性
match result {
    case Fine { value }: handle_success(value)
    case Fail { error }: handle_error(error)
    # 编译器确保所有变体都被处理
}

不可达分支警告

valkyrie
match number {
    case x if x > 0: "positive"
    case x if x >= 0: "non-negative"  # 警告:不可达
    case x: "negative"
}

最佳实践

性能优化

valkyrie
# 将最常见的情况放在前面
match http_status {
    case 200: "OK"                    # 最常见
    case 404: "Not Found"            # 次常见
    case 500: "Internal Server Error" # 较少见
    case status: "Status: ${status}"  # 其他情况
}

# 避免复杂的守卫条件
match user {
    case { role: "admin" }: handle_admin(user)
    case { role: "user" }: handle_user(user)
    case other: handle_other(other)
}

可读性优化

valkyrie
# 使用有意义的变量名
match request {
    case { method: "GET", path }: handle_get(path)
    case { method: "POST", path, body }: handle_post(path, body)
    case { method: unsupported_method, .. }: 
        error("Unsupported method: ${unsupported_method}")
}

# 适当使用注释
match complex_data {
    # 处理标准格式
    case { version: "1.0", data }: process_v1(data)
    # 处理新格式
    case { version: "2.0", data }: process_v2(data)
    # 向后兼容
    case legacy_data: migrate_and_process(legacy_data)
}

错误处理模式

链式错误处理

valkyrie
let result = input
    .parse()
    .match {
        case Fine { value: parsed }: parsed
        case Fail { error }: return Fail { error: "Parse error: ${error}" }
    }
    .validate()
    .match {
        case Fine { value: validated }: validated
        case Fail { error }: return Fail { error: "Validation error: ${error}" }
    }

错误聚合

valkyrie
match (result1, result2, result3) {
    case (Fine { value: a }, Fine { value: b }, Fine { value: c }): Fine { value: (a, b, c) }
    case (Fail { error: e }, _, _): Fail { error: e }
    case (_, Fail { error: e }, _): Fail { error: e }
    case (_, _, Fail { error: e }): Fail { error: e }
}

Released under the MIT License.