条件による制御構造は候補の中から選択を行ないます。Emacs
Lispは5つの条件フォームをもちます。if
は他の言語のものとほとんど同じです。when
とunless
はif
の変種です。cond
は一般化されたcase命令です。cond
を汎用化したものがpcase
です(パターンマッチングによる条件を参照)。
if
はconditionの値にもとづきthen-formとelse-formsを選択する。評価されたconditionが非nil
ならthen-formが評価されて結果がリターンされる。それ以外ならelse-formsがテキスト順に評価されて最後のフォームの値がリターンされる(if
のelseパートは暗黙のprogn
の例である。順序を参照)。
conditionの値がnil
でelse-formsが与えられなければ、if
はnil
をリターンする。
選択されなかったブランチは決して評価されない — 無視される —
ので、if
はスペシャルフォームである。したがって以下の例ではprint
が呼び出されることはないのでtrue
はプリントされない。
(if nil (print 'true) 'very-false) ⇒ very-false
これはelse-formsがなく、複数のthen-formsが可能なif
の変種である。特に、
(when condition a b c)
は以下と完全に等価である
(if condition (progn a b c) nil)
これはthen-formがないif
の変種です:
(unless condition a b c)
は以下と完全に等価である
(if condition nil a b c)
cond
は任意個数の選択肢から選択を行なう。cond
内の各clauseはリストでなければならない。このリストのCARはconditionで、(もしあれば)残りの要素はbody-formsとなる。したがってclauseは以下のようになる:
(condition body-forms...)
cond
は各clauseのconditionを評価することにより、テキスト順でclauseを試みる。conditionの値が非nil
ならそのclauseは成り立つ。その後にcond
はそのclauseのbody-formsを評価して、body-formsの最後の値をリターンする。残りのclauseは無視される。
conditionの値がnil
ならそのclauseは失敗して、cond
は次のclauseに移動してそれのconditionを試みる。
clauseは以下のようにも見えるかもしれない:
(condition)
conditionがテストされたときに非nil
なら、cond
フォームはconditionの値をリターンする。
すべてのconditionがnil
に評価された場合 —
つまりすべてのclauseが不成立なら、cond
はnil
をリターンする。
以下の例はx
の値が数字、文字列、バッファー、シンボルなのかをテストする4つのclauseをもつ:
(cond ((numberp x) x) ((stringp x) x) ((bufferp x) (setq temporary-hack x) ; 1つのclauseに (buffer-name x)) ; 複数bodyフォーム ((symbolp x) (symbol-value x)))
前のclauseが不成立のとき最後の条項を実行したいときがよくある。これを行なうには(t
body-forms)
のように、conditionの最後のclauseにt
を使用する。フォームt
はt
に評価され決してnil
にならないので、このclauseが不成立になることはなく最終的にcond
はこのclauseに到達する。たとえば:
(setq a 5) (cond ((eq a 'hack) 'foo) (t "default")) ⇒ "default"
このcond
式はa
の値がhack
ならfoo
、それ以外は文字列"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))
このテーマにはバリエーションがいくつかあり、以下にそれらを概略します。
Evaluate each binding in varlist, stopping if a binding value is
nil
. If all are non-nil
, return the value of then-form,
otherwise the last form in else-forms.
Each element of varlist
has the form (symbol value-form)
: value-form is evaluated and symbol is
locally bound to the result. Bindings are sequential, as in let*
(see ローカル変数). As a special case, symbol can be omitted
if only the test result of value-form is of interest: value-form
is evaluated and checked for nil
, but its value is not bound.
Evaluate each binding in varlist, stopping if a binding value is
nil
. If all are non-nil
, return the value of the last form in
then-forms.
varlist has the same form as in if-let*
: Each element of
varlist
has the form (symbol value-form)
, in
which value-form is evaluated and symbol is locally bound to the
result. Bindings are sequential, as in let*
(see ローカル変数). As a special case, symbol can be omitted if only the
test result of value-form is of interest: value-form is
evaluated and checked for nil
, but its value is not bound.
Evaluate each binding in varlist, stopping if a binding value is
nil
. If all are non-nil
, return the value of the last form in
then-forms, or, if there are no then-forms, return the value of
the last binding.
varlist has the same form as in if-let*
: Each element of
varlist
has the form (symbol value-form)
, in
which value-form is evaluated and symbol is locally bound to the
result. Bindings are sequential, as in let*
(see ローカル変数). As a special case, symbol can be omitted if only the
test result of value-form is of interest: value-form is
evaluated and checked for nil
, but its value is not bound.
フォームを評価したリターン値が目的の場合にはand
とand-let*
、フォーム評価による副作用が目的であってリターン値は無視する場合にはwhen
かwhen-let*
という慣習にしたがうLispプログラマーもいます。
A similar macro exists to run a loop until one binding evaluates to
nil
:
Evaluate each binding in spec in turn, stopping if a binding value is
nil
. If all are non-nil
, execute then-forms, then
repeat the loop. Note that when the loop is repeated, the value-forms
in spec are re-evaluated and the bindings are established anew.
varlist has the same form as in if-let*
: Each element of
varlist
has the form (symbol value-form)
, in
which value-form is evaluated and symbol is locally bound to the
result. Bindings are sequential, as in let*
(see ローカル変数). As a special case, symbol can be omitted if only the
test result of value-form is of interest: value-form is
evaluated and checked for nil
, but its value is not bound.
The return value of while-let
is always nil
.