CppCon 2015 学习:Simple, Extensible Pattern Matching in C++14
什么是 Pattern Matching(模式匹配)
❝ 模式匹配就是一种“描述式”的写法,不需要你手动判断、提取数据,而是直接描述你希望的数据结构是什么样子,系统自动判断并提取。❞
你给的定义拆解:
✴ Instead of explicitly extracting and testing…
传统写法是这样的:
if (shape.type == "circle") {double r = shape.radius;// do something
} else if (shape.type == "rectangle") {double w = shape.width;double h = shape.height;// do something
}
这种方式需要你先判断类型,再显式地提取数据成员。这就是“explicitly extracting and testing”。
Pattern Matching 改写为:
match shape {Circle(r) => { /* use r directly */ },Rectangle(w, h) => { /* use w and h directly */ },
}
你直接描述想要的结构,比如一个 Circle(r)
,系统就会帮你提取 r
,而不需要你去“访问属性”。
你也可以把它想象成这样:
正则表达式 | 模式匹配 |
---|---|
匹配文本结构 | 匹配数据结构 |
提取子串 | 提取子字段 |
(\d+)-(\w+) | Rectangle(w, h) |
if (x matches pattern) | match x { pattern => ... } |
总结一句话
模式匹配 = 数据结构的“正则表达式”
它让你声明你想要的数据形状,并在匹配时自动提取内容并处理。
这是一个 经典的 C++ switch-case
语句例子,它展示了 在运行时对整型变量 i
进行分支匹配的能力。下面是对这段代码的逐行解释:
原始代码:
switch (i) {case 1:std::cout << "one";break;case 2:std::cout << "two";break;case 3:std::cout << "three";break;default:std::cout << "unknown";
}
代码含义:
switch (i)
:根据变量i
的值,进入匹配的分支。case 1:
:如果i == 1
,输出 “one”。case 2:
:如果i == 2
,输出 “two”。case 3:
:如果i == 3
,输出 “three”。default:
:如果不匹配任何case
,输出 “unknown”。
特点:
特性 | 说明 |
---|---|
运行时匹配 | 编译时无法确定分支走向,必须等 i 的值在运行时才知道。 |
整型值支持 | 传统 switch 只支持 int 或 enum 类型。 |
手动 break | 每个分支后需要加 break 防止“贯穿执行”到下一个 case。 |
default 备选 | default 分支相当于“else”逻辑。 |
类比模式匹配(Pattern Matching)
switch
是一种最基础的“模式匹配”形式,但功能非常有限:
特性 | switch | 模式匹配(如 Rust) |
---|---|---|
支持数据结构 | 仅支持整数值匹配 | 支持结构体/枚举/元组等 |
自动提取字段 | 不能 | 自动解构(如 Point(x, y) ) |
表达式形式 | 语句形式,不能直接赋值 | 可作为表达式返回值 |
这段 Haskell 代码是关于 模式匹配(Pattern Matching) 和 元组 / 列表的分解式函数定义,下面是对每一部分的详细解释和理解:
一、函数定义:提取三元组中的元素
first :: (a, b, c) -> a
first (x, _, _) = x
- 类型签名
:: (a, b, c) -> a
表示:函数first
接收一个三元组(a, b, c)
,返回其中的第一个元素a
。 (x, _, _) = x
使用模式匹配提取第一个值,忽略其他两个(用_
表示不关心)。
类似的:
second :: (a, b, c) -> b
second (_, y, _) = y
third :: (a, b, c) -> c
third (_, _, z) = z
这些函数从三元组中提取第二、第三个元素。
二、列表分析函数 describeList
describeList :: [a] -> String
describeList xs = "The list is " ++ case xs of[] -> "empty."[x] -> "a singleton list."xs -> "a longer list."
类型说明:
:: [a] -> String
表示该函数接收一个任意类型元素的列表,返回一个String
描述。
逻辑说明:
case xs of
使用 模式匹配 来分析xs
的结构:模式 匹配条件 返回文本 []
空列表 "empty."
[x]
只包含一个元素的列表 "a singleton list."
xs
(默认)多于一个元素的列表 "a longer list."
示例:
describeList [] -- "The list is empty."
describeList [42] -- "The list is a singleton list."
describeList [1,2,3] -- "The list is a longer list."
三、总结
概念 | 说明 |
---|---|
模式匹配(Pattern Matching) | Haskell 的核心特性,可在函数参数、case 表达式中直接分解数据结构。 |
元组分解 | (x, _, _) 形式表示从三元组中提取出想要的元素。 |
列表匹配 | [] , [x] , xs 等用于分别匹配空列表、单元素列表、多元素列表。 |
表达式匹配 + 拼接 | 使用 ++ 拼接字符串,用 case 对输入做结构匹配判断并产生不同返回值。 |
你给出的这段代码是 Rust 中 模式匹配(match
) 的典型用法。下面是对它的完整解释:
代码回顾
let x = 1;
match x {1 | 2 => println!("one or two"), 3 => println!("three"),_ => println!("anything"),
}
逐行解释
let x = 1;
- 这行代码定义了一个变量
x
,它的值为1
。
match x { ... }
这是 Rust 中的 模式匹配结构,类似于其他语言中的 switch
,但更强大。
Rust 的 match
表达式根据变量的值与不同的 模式(patterns) 进行匹配,一旦匹配成功,就执行对应的代码块。
分支解释:
1 | 2 => println!("one or two"),
1 | 2
表示:如果x
是 1 或 2,就执行这一分支。|
是逻辑“或”的模式匹配用法(不是布尔操作)。
3 => println!("three"),
- 当
x == 3
时匹配这个分支。
_ => println!("anything"),
_
是通配符,表示“其他任何情况”。- 如果
x
不是 1、2 或 3,就会匹配这个分支。
本例中运行结果
由于 x = 1
,因此匹配第一个分支:
one or two
总结
结构 | 说明 | |
---|---|---|
match | 用于模式匹配变量的值 | |
` | ` | 匹配多个值中的任一个 |
_ | 匹配所有未被显式列出的情况 | |
=> | 分支体开始 |
小技巧
match
在 Rust 中是表达式(expression),所以它可以返回值。- 分支是穷尽性匹配,编译器会强制你覆盖所有可能性,或使用
_
兜底。 match
也可以解构结构体、枚举、元组、引用等,功能非常强大。
这段 Rust 代码展示了 模式匹配(pattern matching) 在元组(tuple)上的用法。我们来逐步理解它:
原始代码
let tup = (1, 0);
match tup {(0, 0) => println!("both zero"),(x, 0) => println!("{} and zero", x),(0, y) => println!("zero and {}", y),_ => println!("did not match"),
}
变量定义
let tup = (1, 0);
- 创建了一个元组变量
tup
,其值是(1, 0)
。
match
分支解析
Rust 的 match
会从上到下尝试匹配每个模式:
① (0, 0) => println!("both zero")
- 匹配元组两个元素都是 0 的情况。
- 当前是
(1, 0)
,不匹配。
② (x, 0) => println!("{} and zero", x)
- 匹配元组第二个元素为
0
,第一个元素任意(绑定为x
)。 - 当前是
(1, 0)
,匹配成功。 - 执行:
println!("{} and zero", x)
,输出:
1 and zero
匹配成功后,match
结构结束,不会继续匹配下面的分支。
③ (0, y) => println!("zero and {}", y)
- 匹配第一个是 0,第二个任意的元组。
- 当前不匹配,因此不会执行。
④ _ => println!("did not match")
_
是通配符:匹配所有未匹配到的情况。- 本例中没走到这一分支。
运行结果
1 and zero
小结
模式 | 含义 | 匹配 (1, 0) 吗? |
---|---|---|
(0, 0) | 两个都是 0 | 否 |
(x, 0) | 第二个是 0,第一个绑定为 x | 是 |
(0, y) | 第一个是 0,第二个绑定为 y | 否 |
_ | 其他任何情况(兜底) | 不会执行 |
如果你想学习更多 Rust 中的模式匹配技巧,例如: |
- 匹配结构体和枚举
- 使用
match guard
(例如if x > 0
) - 模式绑定的细节(如
@
绑定)
这段 Rust 代码展示了如何使用 枚举(enum
)和模式匹配(match
) 来处理不同类型的消息(Message
)。我们来逐步分析和理解它:
枚举定义
enum Message {Quit,ChangeColor(i32, i32, i32),Move { x: i32, y: i32 },Write(String),
}
这定义了一个 Message
类型,它有 4 种变体(variants):
变体名 | 类型 | 描述 |
---|---|---|
Quit | 无数据 | 退出指令 |
ChangeColor | 包含三个 i32 参数 | 改变颜色(RGB) |
Move | 是一个具名结构体样式的变体 | 移动到 (x, y) 坐标位置 |
Write | 包含一个 String | 输出字符串 |
process_message
函数
fn process_message(msg: Message) {match msg {Message::Quit => quit(),Message::ChangeColor(r, g, b) => change_color(r, g, b),Message::Move { x: x, y: y } => move_cursor(x, y),Message::Write(s) => println!("{}", s),};
}
该函数接收一个 Message
类型的参数,然后使用 match
匹配它是哪种变体,并执行相应的操作。
逐个分支解释
1. Message::Quit => quit()
- 如果是
Quit
变体,调用quit()
函数。 - 不包含任何数据。
2. Message::ChangeColor(r, g, b) => change_color(r, g, b)
- 如果是
ChangeColor
,提取出三个整数,绑定为r, g, b
,然后传给change_color
函数。 - 这是 位置匹配(positional matching)。
3. Message::Move { x: x, y: y } => move_cursor(x, y)
- 如果是
Move
变体,它是具名字段结构体形式。 - 提取
x
和y
字段,传入move_cursor(x, y)
。 - 可以简写为
{ x, y }
而不是{ x: x, y: y }
。
4. Message::Write(s) => println!("{}", s)
- 如果是
Write(String)
,提取出字符串s
,并打印。
简写优化(推荐写法)
你可以把 x: x, y: y
简写为 x, y
:
Message::Move { x, y } => move_cursor(x, y)
示例调用
process_message(Message::Quit);
process_message(Message::Write("Hello!".to_string()));
process_message(Message::Move { x: 10, y: 20 });
process_message(Message::ChangeColor(255, 0, 0));
小结
这个例子体现了 Rust 的模式匹配能力:
- 适用于结构体、枚举、元组等
- 自动解构并绑定变量
- 简洁且类型安全
如果你来自其他语言(如 C++、Java),可以把这理解为: enum
是 tagged union + 类型安全match
类似switch
,但更强大,可解构结构体和元组
后面应该是介绍这个c++怎么实现的看不懂 知道模式匹配这个东西就行
https://github.com/jbandela/simple_match