本小节介绍 反引号风格模式(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 匹配 qpat1、cdr 匹配 qpat2 时,匹配成功。这种形式可以很自然地推广到列表,如
(qpat1 qpat2 …)。
[qpat1 qpat2 … qpatm]当 expval 是一个长度为 m 的向量(vector),并且其第 0 到第 (m-1) 个元素分别匹配 qpat1、qpat2、…、qpatm 时,匹配成功。
symbolkeywordnumberstring当 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),才能正确捕获 body 和 arg(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 开头的三元素列表,然后提取第二个和第三个元素,并将它们分别绑定到符号 x 和 y。这一过程称为解构(destructuring),详见 使用 pcase 模式进行解构。分支主体会对 x 和 y 求值并将结果相加。
类似地,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