11.2 条件

条件による制御構造は候補の中から選択を行ないます。Emacs Lispは5つの条件フォームをもちます。ifは他の言語のものとほとんど同じです。whenunlessifの変種です。condは一般化されたcase命令です。condを汎用化したものがpcaseです(パターンマッチングによる条件を参照)。

Special Form: if condition then-form else-forms…

ifconditionの値にもとづきthen-formelse-formsを選択する。評価されたconditionが非nilならthen-formが評価されて結果がリターンされる。それ以外ならelse-formsがテキスト順に評価されて最後のフォームの値がリターンされる(ifelseパートは暗黙のprognの例である。順序を参照)。

conditionの値がnilelse-formsが与えられなければ、ifnilをリターンする。

選択されなかったブランチは決して評価されない — 無視される — ので、ifはスペシャルフォームである。したがって以下の例ではprintが呼び出されることはないのでtrueはプリントされない。

(if nil
    (print 'true)
  'very-false)
⇒ very-false
Macro: when condition then-forms…

これはelse-formsがなく、複数のthen-formsが可能なifの変種である。特に、

(when condition a b c)

は以下と完全に等価である

(if condition (progn a b c) nil)
Macro: unless condition forms…

これはthen-formがないifの変種です:

(unless condition a b c)

は以下と完全に等価である

(if condition nil
   a b c)
Special Form: cond clause…

condは任意個数の選択肢から選択を行なう。cond内の各clauseはリストでなければならない。このリストのCARconditionで、(もしあれば)残りの要素は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の値をリターンする。

すべてのconditionnilに評価された場合 — つまりすべてのclauseが不成立なら、condnilをリターンする。

以下の例は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を使用する。フォームttに評価され決してnilにならないので、このclauseが不成立になることはなく最終的にcondはこのclauseに到達する。たとえば:

(setq a 5)
(cond ((eq a 'hack) 'foo)
      (t "default"))
⇒ "default"

このcond式はaの値がhackならfoo、それ以外は文字列"default"をリターンする。

すべての条件構文はcondifのいずれかで表すことができます。したがってどちらを選択するかはスタイルの問題になります。たとえば:

(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))

このテーマにはバリエーションがいくつかあり、以下にそれらを概略します。

Macro: 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かどうかのチェックは行われるものの値はバインドされない。

Macro: when-let* varlist then-forms...

varlist内のバインディングをそれぞれ評価して、値がnilになるバインディングがあれば停止する。すべて非nilならthen-formの最後のフォームの値をリターンする。

varlistif-let*と同じフォームをもつ。varlistの要素はそれぞれ(symbol value-form)というフォームをもち、value-formが評価された結果がsymbolにローカルにバインドされる。バインディングはlet* (ローカル変数を参照)のように順に行われる。value-formの結果だけが重要な場合には、特別にsymbolを省略できる。この場合にはvalue-formを評価してnilのチェックはするが、値はバインドされない。

Macro: and-let* varlist then-forms...

varlist内のバインディングをそれぞれ評価して、値がnilのバインディングがあれば停止する。すべてが非nilの場合にはthen-formsの最後のフォームの値、then-formsがなければ最後のバインディングの値をリターンする。

varlistif-let*と同じフォームをもつ。varlistの要素はそれぞれ(symbol value-form)というフォームをもち、value-formが評価された結果がsymbolにローカルにバインドされる。バインディングはlet* (ローカル変数を参照)のように順に行われる。value-formの結果だけが重要な場合には、特別にsymbolを省略できる。この場合にはvalue-formを評価してnilのチェックはするが、値はバインドされない。

フォームを評価したリターン値が目的の場合にはandand-let*、フォーム評価による副作用が目的であってリターン値は無視する場合にはwhenwhen-let*という慣習にしたがうLispプログラマーもいます。

いずれかのバインディングがnilに評価されるまでループを実行する似たようなマクロもあります:

Macro: while-let spec then-forms...

spec内のバインディングをそれぞれ順番に評価して、値がnilのバインディングがあれば停止する。すべてnil以外であれば、then-formsを実行した後にループを繰り返す。ループを繰り返す際にはspec内のvalue-formsが再び評価されて、新たなバインディングが確立されることに注意。

varlistif-let*と同じフォームをもつ。varlistの要素はそれぞれ(symbol value-form)というフォームをもち、value-formが評価された結果がsymbolにローカルにバインドされる。バインディングはlet* (ローカル変数を参照)のように順に行われる。value-formの結果だけが重要な場合には、特別にsymbolを省略できる。この場合にはvalue-formを評価してnilのチェックはするが、値はバインドされない。

while-letは常に値nilをリターンする。


This page has generated for branch:work/emacs-30_69b16e5c63840479270d32f58daea923fe725b90, commit:8c196e027afcda4529432b01ae733033b6ca1270 to check Japanese translation.