条件による制御構造は候補の中から選択を行ないます。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.