Next: 組み合わせ条件の構築, Previous: 順序, Up: 制御構造 [Contents][Index]
条件による制御構造は候補の中から選択を行ないます。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))
このテーマにはバリエーションがいくつかあり、以下にそれらを概略します。
varlist内のバインディングを、let*
(ローカル変数を参照)のようにそれぞれ順番に評価して、値がnil
になるバインディングがあれば停止する。すべて非nil
ならthen-form、それ以外ではelse-formsの最後のフォームの値をリターンする。
if-let*
と同様だがelse-formsがない。
when-let*
と同様だが、もしthen-formsがなくすべてのバインディングが非nil
に評価された場合には、最後のバインディングの値をリターンする点が異なる。
when-let
と同様だが、spec内のバインディングがnil
になるまで繰り返す。リターン値は常にnil
。
フォームを評価したリターン値が目的の場合にはand
とand-let*
、フォーム評価による副作用が目的であってリターン値は無視する場合にはwhen
かwhen-let*
という慣習にしたがうLispプログラマーもいます。
Next: 組み合わせ条件の構築, Previous: 順序, Up: 制御構造 [Contents][Index]