Условные конструкции
Условные управляющие конструкции выбирают один из вариантов. В Emacs Lisp есть пять условных форм: if, которая во многом похожа на конструкции в других языках; when и unless, являющиеся вариантами if; cond, представляющая собой обобщённую конструкцию выбора; и pcase, являющаяся обобщением cond (см. Условная конструкция с сопоставлением с образцом).
- Специальная форма: if condition then-form else-forms…
-
ifвыбирает между then-form и else-forms в зависимости от значения condition. Если вычисленное значение condition отлично отnil, вычисляется then-form и возвращается результат. В противном случае else-forms вычисляются в текстовом порядке, и возвращается значение последней формы. (Часть else вif— это пример неявногоprogn. См. Последовательное выполнение.)Если значение condition равно
nilи else-forms не заданы,ifвозвращаетnil.if— специальная форма, поскольку невыбранная ветвь никогда не вычисляется — она игнорируется. Поэтому в этом примереtrueне выводится, так какprintникогда не вызывается:(if nil (print 'true) 'very-false) ⇒ very-false
- Макрос: when condition then-forms…
-
Это вариант
ifбез else-forms и, возможно, с несколькими then-forms. В частности,(when condition a b c)
полностью эквивалентна
(if condition (progn a b c) nil)
- Макрос: unless condition forms…
-
Это вариант
ifбез then-form:(unless condition a b c)
полностью эквивалентна
(if condition nil a b c)
- Специальная форма: cond clause…
-
condвыбирает один из произвольного числа вариантов. Каждое clause вcondдолжно быть списком. CAR этого списка — это condition; оставшиеся элементы, если они есть, — это body-forms. Таким образом, предложение выглядит так:(condition body-forms...)
condпроверяет предложения в текстовом порядке, вычисляя condition каждого предложения. Если значение condition отлично отnil, предложение выполняется успешно; тогдаcondвычисляет его body-forms и возвращает значение последней из форм body-forms. Все оставшиеся предложения игнорируются.Если значение condition равно
nil, предложение не выполняется, иcondпереходит к следующему предложению, проверяя его condition.Предложение также может выглядеть так:
(condition)
В таком случае, если при проверке condition отлично от
nil, формаcondвозвращает значение condition.Если результат вычисления каждого condition равен
nil, то есть ни одно предложение не выполняется,condвозвращаетnil.В следующем примере четыре предложения проверяют случаи, когда значение
xявляется числом, строкой, буфером и символом соответственно:(cond ((numberp x) x) ((stringp x) x) ((bufferp x) (setq temporary-hack x) ; multiple body-forms (buffer-name x)) ; in one clause ((symbolp x) (symbol-value x)))Часто требуется выполнить последнее предложение, если ни одно из предыдущих не выполнилось успешно. Для этого в качестве condition последнего предложения используют
t, например:(t body-forms). Формаtвычисляется вt, которое никогда не бываетnil, поэтому это предложение никогда не завершается неудачей, если только до него доходит выполнениеcond. Например:(setq a 5) (cond ((eq a 'hack) 'foo) (t "default")) ⇒ "default"Это выражение
condвозвращаетfoo, если значениеaравноhack, а в противном случае возвращает строку"default".
Любую условную конструкцию можно выразить с помощью cond или if. Поэтому выбор между ними зависит от стиля. Например:
(if a b c) ≡ (cond (a b) (t c))
Условные конструкции удобно сочетать с привязкой переменных. Часто требуется вычислить значение, а затем выполнить с ним какое-либо действие, если оно отлично от nil. Самый простой способ — просто написать, например:
(let ((result1 (do-computation)))
(when result1
(let ((result2 (do-more result1)))
(when result2
(do-something result2)))))
Поскольку это очень распространённый шаблон, в Emacs есть несколько макросов, которые упрощают его использование и делают код более читаемым. Вместо этого приведённый выше код можно записать так:
(when-let* ((result1 (do-computation))
(result2 (do-more result1)))
(do-something result2))
Существует несколько вариантов этого подхода, описанных ниже.
- Макрос: if-let* varlist then-form else-forms...
-
Вычисляет каждую привязку в varlist, останавливаясь, если значение привязки равно
nil. Если все значения отличны отnil, возвращает значение then-form, иначе — последней формы в else-forms.Каждый элемент
varlistимеет вид(symbol value-form): value-form вычисляется, а symbol локально связывается с результатом. Привязки создаются последовательно, как вlet*(см. Локальные переменные). Если важен только результат проверки value-form, укажите_вместо symbol. Тогда value-form вычисляется и проверяется наnil, но его значение не привязывается. В особом случае элементvarlistможет иметь вид простоsymbol, что означает проверку текущей привязки symbol наnil.
- Макрос: when-let* varlist then-forms...
-
Вычисляет каждую привязку в varlist, останавливаясь, если значение привязки равно
nil. Если все значения отличны отnil, возвращает значение последней формы в then-forms.varlist имеет ту же форму, что и в
if-let*: каждый элементvarlistимеет вид(symbol value-form), где value-form вычисляется, а symbol локально связывается с результатом. Привязки создаются последовательно, как вlet*(см. Локальные переменные). Если важен только результат проверки value-form, укажите_вместо symbol. Тогда value-form вычисляется и проверяется наnil, но его значение не привязывается. В особом случае элементvarlistможет иметь вид простоsymbol, что означает проверку текущей привязки symbol наnil.
- Макрос: and-let* varlist then-forms...
-
Вычисляет каждую привязку в varlist, останавливаясь, если значение привязки равно
nil. Если все значения отличны отnil, возвращает значение последней формы в then-forms или, если then-forms отсутствуют, значение последней привязки.varlist имеет ту же форму, что и в
if-let*: каждый элементvarlistимеет вид(symbol value-form), где value-form вычисляется, а symbol локально связывается с результатом. Привязки создаются последовательно, как вlet*(см. Локальные переменные). Если важен только результат проверки value-form, укажите_вместо symbol. Тогда value-form вычисляется и проверяется наnil, но его значение не привязывается. В особом случае элементvarlistможет иметь вид простоsymbol, что означает проверку текущей привязки symbol наnil.
Некоторые программисты на Lisp придерживаются соглашения, согласно которому and и and-let* используются для форм, вычисляемых ради возвращаемого значения, а when и when-let* — для форм, вычисляемых ради побочного эффекта, при этом возвращаемые значения игнорируются.
С точки зрения стиля лучше не использовать эти макросы для привязки значений, которые всегда будут отличны от nil. Например, предположим, что foo-p и bar-p могут возвращать nil, и в этом случае выполнение кода не должно продолжаться. Вместо
(when-let* ((foo-val (foo-p))
(num (+ 2 foo-val))
(bar-val (bar-p num)))
...)
лучше использовать
(when-let* ((foo-val (foo-p)))
(let ((num (+ 2 foo-val))
(when-let* ((bar-val (bar-p num)))
...)))
так как так яснее, что выполнение кода зависит от того, отличны ли foo-val и bar-val от nil, но не зависит непосредственно от num, которое к тому же никогда не может быть nil.
Макроса cond-let* нет, поскольку расширить структуру, общую для if-let*, when-let* и and-let*, на случай cond непросто. Однако для достижения похожего результата можно использовать предложения bind-and* макроса cond* (см. Макрос cond*):
(cond* ((bind-and* (result1 (do-computation))
(result2 (do-more result1)))
(do-something result2))
...)
Существует похожий макрос для выполнения цикла до тех пор, пока одна из привязок не вычислится в nil:
- Макрос: while-let spec then-forms...
-
По очереди вычисляет каждую привязку в spec, останавливаясь, если значение привязки равно
nil. Если все значения отличны отnil, выполняет then-forms, а затем повторяет цикл. Обратите внимание: при повторении цикла формы value-forms в spec вычисляются заново, а привязки создаются снова.varlist имеет ту же форму, что и в
if-let*: каждый элементvarlistимеет вид(symbol value-form), где value-form вычисляется, а symbol локально связывается с результатом. Привязки создаются последовательно, как вlet*(см. Локальные переменные). Если важен только результат проверки value-form, укажите_вместо symbol. Тогда value-form вычисляется и проверяется наnil, но его значение не привязывается. В особом случае элементvarlistможет иметь вид простоsymbol, что означает проверку текущей привязки symbol наnil.Возвращаемое значение
while-letвсегда равноnil.
Copyright © 1990-1996, 1998-2026 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Conditionals.html