11.4.4 Деструктуризация с шаблонами pcase

Шаблоны pcase не только выражают условие относительно формы объектов, которые они могут сопоставить, но они также могут извлекать подполя этих объектов. Например, мы можем извлечь 2 элемента из списка, который является значением переменной my-list, с помощью следующего кода:

  (pcase my-list
    (`(add ,x ,y)  (message "Содержит %S и %S" x y)))

Это не только извлечет x и y, но также проверит, что my-list является списком, содержащим ровно 3 элемента, и что его первый элемент — это символ add. Если какое-либо из этих проверок не удастся, pcase немедленно вернет nil, не вызывая message.

Извлечение нескольких значений, хранящихся в объекте, известно как деструктуризация. Использование шаблонов pcase позволяет выполнять привязку деструктуризации, которая похожа на локальную привязку (см. Локальные переменные), но присваивает значения нескольким элементам переменной, извлекая эти значения из объекта совместимой структуры.

Макросы, описанные в этом разделе, используют шаблоны pcase для выполнения привязки деструктуризации. Условие того, что объект имеет совместимую структуру, означает, что объект должен соответствовать шаблону, поскольку только тогда можно извлечь подполя объекта. Например:

  (pcase-let ((`(add ,x ,y) my-list))
    (message "Содержит %S и %S" x y))

делает то же самое, что и предыдущий пример, за исключением того, что он напрямую пытается извлечь x и y из my-list, не проверяя предварительно, является ли my-list списком, имеющим правильное количество элементов и имеющим add в качестве первого элемента.

Точное поведение, когда объект на самом деле не соответствует шаблону, зависит от типов, хотя тело не будет молча пропущено: либо сигнализируется ошибка, либо тело выполняется с некоторыми переменными, привязанными к произвольным значениям, таким как nil. Например, приведенный выше шаблон приведет к тому, что x и y будут извлечены с помощью операций, таких как car или nth, поэтому они получат значение nil, когда my-list слишком короткий. В отличие от этого, с шаблоном вроде `[add ,x ,y], эти же переменные будут извлечены с помощью aref, который выдаст ошибку, если my-list не является массивом или слишком короток.

Шаблоны pcase, полезные для привязок деструктуризации, как правило, являются теми, которые описаны в Шаблонах в стиле обратных кавычек, поскольку они выражают спецификацию структуры объектов, которые будут соответствовать.

Для альтернативного средства привязки деструктуризации см. seq-let.

Макрос: 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"
Макрос: pcase-let* bindings body…

Выполняет привязку деструктуризации переменных в соответствии с bindings, а затем вычисляет body.

bindings — это список привязок вида (pattern exp), где exp — это выражение для вычисления, а pattern — это шаблон pcase. Привязки переменных производятся путем деструктуризации элементов pattern к значениям соответствующих элементов вычисленного exp.

В отличие от pcase-let, но похоже на let*, каждое exp сопоставляется с соответствующим pattern перед обработкой следующего элемента bindings, поэтому привязки переменных, введенные в каждом из bindings, доступны в exp следующих, в дополнение к тому, что они доступны в body.

Макрос: pcase-dolist (pattern list) body…

Выполняет body один раз для каждого элемента list, при этом на каждой итерации выполняя привязку деструктуризации переменных в pattern к значениям соответствующих подполей элемента list. Привязки выполняются так, как если бы это был pcase-let. Когда pattern является простой переменной, это эквивалентно dolist (см. Итерация).

Макрос: pcase-setq pattern value…

Присваивает значения переменным в форме setq, деструктурируя каждое value в соответствии с соответствующим pattern.

Макрос: pcase-lambda lambda-list &rest body

Это похоже на lambda, но позволяет каждому аргументу быть шаблоном. Например, вот простая функция, которая принимает ячейку cons в качестве аргумента:

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