11.4.3 反引号风格模式

本小节介绍 反引号风格模式(backquote-style patterns),这是一组内置模式,能够简化结构化匹配。相关背景知识见 see 模式匹配条件

反引号风格模式是 pcase 中一组功能强大的模式扩展(通过 pcase-defmacro 实现),可以方便地根据 结构expval 进行匹配。

例如,要匹配必须是含两个元素的列表、第一个元素是指定字符串、第二个元素可以是任意值的 expval,可以用核心模式这样写:

(and (pred listp)
     ls
     (guard (= 2 (length ls)))
     (guard (string= "first" (car ls)))
     (let second-elem (cadr ls)))

或者你也可以编写等价的反引号风格模式:

`("first" ,second-elem)

反引号风格模式更加简洁,与 expval 的结构相似,并且无需绑定 ls

反引号风格模式的形式为 `qpat,其中 qpat 可以是以下几种形式:

(qpat1 . qpat2)

expval 是一个序对(cons cell),并且其 car 匹配 qpat1cdr 匹配 qpat2 时,匹配成功。这种形式可以很自然地推广到列表,如 (qpat1 qpat2 …)

[qpat1 qpat2qpatm]

expval 是一个长度为 m 的向量(vector),并且其第 0 到第 (m-1) 个元素分别匹配 qpat1qpat2、…、qpatm 时,匹配成功。

symbol
keyword
number
string

expval 的对应元素与指定的字面量对象(literal object)满足 equal 相等性时,匹配成功。

,pattern

expval 的对应元素匹配 pattern 时,匹配成功。注意,pattern 可以是 pcase 支持的任意类型的模式。(在上面的示例中,second-elem 是一个核心 symbol 模式;因此它能匹配任意值,并通过 let 绑定 second-elem。)

对应元素(corresponding element) 指的是:expval 中,与反引号风格模式里 qpat 所处结构位置完全相同的那一部分。(在上面的示例中,second-elem 的对应元素就是 expval 的第二个元素。)

以下是一个使用 pcase 为简易表达式语言实现解释器的示例(注意:这要求 fn 分支中的 lambda 表达式启用词法绑定(lexical binding),才能正确捕获 bodyarg(see 词法绑定):

(defun evaluate (form env)
  (pcase form
    (`(add ,x ,y)       (+ (evaluate x env)
                           (evaluate y env)))
    (`(call ,fun ,arg)  (funcall (evaluate fun env)
                                 (evaluate arg env)))
    (`(fn ,arg ,body)   (lambda (val)
                          (evaluate body (cons (cons arg val)
                                               env))))
    ((pred numberp)     form)
    ((pred symbolp)     (cdr (assq form env)))
    (_                  (error "Syntax error: %S" form))))

前三个分支使用了反引号风格模式。 `(add ,x ,y) 这个模式会检查 form 是否为一个以符号 add 开头的三元素列表,然后提取第二个和第三个元素,并将它们分别绑定到符号 xy。这一过程称为解构(destructuring),详见 使用 pcase 模式进行解构。分支主体会对 xy 求值并将结果相加。 类似地,call 分支实现了函数调用,fn 分支实现了匿名函数定义。

剩下的分支使用核心模式。 (pred numberp)form 为数字时匹配成功,匹配成功后,主体会对其直接求值。 (pred symbolp)form 为符号时匹配成功,匹配成功后,主体会在 env 中查找该符号并返回其绑定值。 最后,_ 是通配模式,可以匹配任何内容,因此适合用来报告语法错误。

下面是这个小型语言中的几个示例程序,以及它们的求值结果:

(evaluate '(add 1 2) nil)                 ⇒ 3
(evaluate '(add x y) '((x . 1) (y . 2)))  ⇒ 3
(evaluate '(call (fn x (add 1 x)) 2) nil) ⇒ 3
(evaluate '(sub 1 2) nil)                 ⇒ error

emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike