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...

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.

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

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.

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

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.

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

A similar macro exists to run a loop until one binding evaluates to nil:

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

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.


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