11.4.4 使用 pcase 模式进行解构

Pcase 模式不仅能对可匹配对象的结构表达条件,还能提取这些对象的子字段。例如,我们可以通过下面的代码,从变量 my-list 对应的列表中提取 2 个元素:

  (pcase my-list
    (`(add ,x ,y)  (message "Contains %S and %S" x y)))

这段文本的精准简体中文翻译如下(贴合编程技术文档风格,术语统一且语义完整):

这一操作不仅会提取出 xy,还会额外验证 my-list 是否为恰好包含 3 个元素且第一个元素是符号 add 的列表。如果这些验证中有任意一项失败,pcase 会立即返回 nil,且不会调用 message 函数。

从一个对象中提取其存储的多个值的操作被称为 解构(destructuring)。使用 pcase 模式可以实现 解构绑定(destructuring binding)—— 这种绑定方式与局部绑定(see 局部变量)类似,但它并非简单赋值,而是通过从结构匹配的对象中提取值,为一个变量的多个元素分别赋予对应值。

本节所描述的宏会借助 pcase 模式来执行解构绑定。“对象需具备兼容结构” 这一条件,意味着该对象必须匹配对应的模式—— 只有满足这一条件,才能提取出对象的子字段。例如:

  (pcase-let ((`(add ,x ,y) my-list))
    (message "Contains %S and %S" x y))

其作用与前面的示例相同,区别在于:它会直接尝试从 my-list 中提取 xy,而不会先检查 my-list 是否为列表、元素数量是否正确,以及首元素是否为 add

当对象实际并不匹配模式时,具体行为取决于数据类型,但主体代码不会被静默跳过:要么会抛出错误,要么会执行主体,但部分变量会被绑定到 nil 之类的任意值。 例如,上面的模式会通过 carnth 这类操作提取 xy,因此当 my-list 长度不足时,它们会得到 nil。与之相对,如果使用类似 `[add ,x ,y] 这样的模式,这些变量会通过 aref 提取,一旦 my-list 不是数组或长度不足,就会抛出错误。

适用于解构绑定的 pcase 模式通常就是 反引号风格模式 中介绍的那些,因为它们明确描述了待匹配对象的结构规范。

如需了解解构绑定的另一种实现方式,可参见 seq-let

Macro: pcase-let bindings body…

根据 bindings 对变量执行解构绑定,随后对 body 进行求值。

bindings 是一个绑定列表,其中每个绑定项的形式为 (pattern exp)exp 是待求值的表达式,pattern 是一个 pcase 模式。

所有 exp 会先被求值,之后将其与各自对应的 pattern 进行匹配;匹配完成后会生成新的变量绑定,这些变量可在 body 中使用。变量绑定的生成逻辑是:将 pattern 中的元素解构绑定到已求值的 exp 对应元素的值上。

以下是一个简单示例:

(pcase-let ((`(,major ,minor)
	     (split-string "image/png" "/")))
  minor)
     ⇒ "png"
Macro: pcase-let* bindings body…

根据 bindings 对变量执行解构绑定,随后对 body 进行求值。

bindings 是一个绑定列表,其中每个绑定项的形式为 (pattern exp)exp 是待求值的表达式,pattern 是一个 pcase 模式。变量绑定的生成逻辑为:将 pattern 中的元素解构绑定到已求值的 exp 对应元素的值上。

pcase-let 不同(但与 let* 类似),在处理 bindings 的下一个元素之前,会先将每个 exp 与其对应的 pattern 完成匹配。因此,bindings 中前序绑定项所生成的变量,不仅可在 body 中使用,还能在后续绑定项的 exp 表达式中使用。

Macro: pcase-dolist (pattern list) body…

针对 list 中的每个元素执行一次 body;在每次迭代时,会将 pattern 中的变量解构绑定到 list 当前元素对应子字段的值上。这些绑定的执行逻辑与 pcase-let 一致。当 pattern 是一个简单变量时,该宏的效果等价于 dolist(see 迭代)。

Macro: pcase-setq pattern value…

setq 的形式为变量赋值,同时会根据每个 pattern(各自对应的模式)对 value 执行解构操作。

Macro: pcase-lambda lambda-list &rest body

该宏的作用与 lambda 类似,但允许每个参数都作为一个模式使用。例如,以下是一个接收序对(cons cell)作为参数的简单函数:

(setq fun
      (pcase-lambda (`(,key . ,val))
        (vector key (* val 10))))
(funcall fun '(foo . 2))
    ⇒ [foo 20]

emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike