В этом подразделах описываются шаблоны в стиле обратных кавычек, набор встроенных шаблонов, которые упрощают структурное сопоставление. Для справки см. Условное сопоставление шаблонов.
Шаблоны в стиле обратных кавычек — это мощное расширение шаблонов 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, чей car
сопоставляется с qpat1, а чей cdr сопоставляется с qpat2.
Это легко обобщается до списков, как в
(qpat1 qpat2 …).
[qpat1 qpat2 … qpatm]Сопоставляется, если expval является вектором длиной m,
чьи элементы с 0 по (m-1) сопоставляются с
qpat1, qpat2 … qpatm, соответственно.
symbolkeywordnumberstringСопоставляется, если соответствующий элемент expval равен указанному литеральному объекту.
,patternСопоставляется, если соответствующий элемент expval
сопоставляется с pattern.
Обратите внимание, что pattern может быть любым типом, который
поддерживает pcase. (В приведенном выше примере second-elem
является базовым шаблоном symbol; следовательно, он соответствует
лючему и привязывает second-elem.)
Соответствующий элемент — это часть expval,
которая находится в той же структурной позиции, что и структурная позиция
qpat в шаблоне в стиле обратных кавычек.
(В приведенном выше примере соответствующим элементом second-elem
является второй элемент expval.)
Вот пример использования pcase для реализации простого
интерпретатора для небольшого языка выражений
(обратите внимание, что для корректного захвата body и arg
в клаузе fn требуется лексическая привязка):
(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 соответственно. Это называется деструктуризацией, см. Деструктуризация с шаблонами 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