値の比較


真と偽

より高度なプログラムを作るには、プログラムの流れを分岐させたり、繰り返させるような制御が必要になります。 このような制御を行うには、その前に必ず値を比較し、その結果が正しいかどうかを調べなければなりません。 つまる「〜であれば〜をする。そうでなければ〜をする」という動作をプログラムで実現させるには、その条件に適合するかどうかを数的に調べなければならないのです。

数値が同じ値かどうかなどを調べるには、やはり関数を使います。 基本的な比較関数は、=/=<><=>=、などがあります。

= number &rest more-numbers
/= number &rest more-numbers
< number &rest more-numbers
> number &rest more-numbers
<= number &rest more-numbers
>= number &rest more-numbers

関数比較
=等しい
/=等しくない
<より小さい
>より大きい
<=より小さいか等しい
>=より大きいか等しい

これらの関数は、最初の引数と、その後に続く任意の数の引数を条件で比較し、その結果が正しければ t を返し、そうでなければ nil を返します。 t と nil は変数として使えない特殊な定数です。 単純に、t と nil という独立した値であると考えてください。

例えば、(= 10 10) であれば、数値 10 と 10 は等しいので、この関数の結果は t となります。 同様に (= 1 10) であれば、数値 1 と 10 は等しくないので、結果は nil となります。 このように、定数で比較すれば計算するまでもなく一目瞭然ですが、変数の内容が条件を満たしているかどうかを調べるときに、この比較関数が役に立つのです。

> (setq x 10)
10
> (= x 100)
NIL
> (/= x 100)
T
> (< x 100)
T
> (> x 100)
NIL

この例を見れば、変数 x に保存されている値は 100 ではなく、100 より小さく、100 以上ではないということがわかります。

これらの比較関数は、第二引数移行に任意の数の条件を指定することができます。 例えば (= a b c d) と指定すれば、変数 a の値が変数 b と等しく、かつ変数 a が変数 c と等しく、かつ変数 a が変数 d と等しく、かつ変数 b が変数 c と等しく、かつ変数 b が変数 d と等しく、かつ変数 c が変数 d と等しい場合にのみ t が返され、そうでなければ nil となります。

同様に (< a b c) と指定すれば、変数 a の値が変数 b よりも小さく、変数 a の値が変数 c よりも小さく、変数 b の値が変数 c よりも小さければ t 、そうでなければ nil となるのです。

> (/= 1 2 3)
T
> (/= 1 1 2)
NIL
> (< 1 2 3)
T
> (< 2 2 3)
NIL
> (<= 2 2 3)
T

ただし、この書き方は直観的ではないのであまりお勧めできません。 ゲームの当たり判定など(LISP でゲームを作る挑戦者がいるかどうかは別として)、一度にたくさんの比較をしなければならない場合などを除いて、通常は二項演算のように二つの引数だけで書いたほうが見やすいと思われます。


複数の式の比較

上記のような手法で得られた比較結果を組み合わせることで、より複雑な条件を指定することができます。 例えば「二つの条件式がともに t であれば」という条件や、「二つの条件式のうち一方が t であれば」というような条件です。

関数 notandor を使えば、こうした複雑な条件を実現させることができます。 正しくは、not は関数ですが、and と or はマクロと呼ばれる式(フォーム)です。 マクロについては後述しますが、使い方は関数や特別式と同じです。

not x
and {form}*
or {form}*

not 関数は単純に、x に nil が渡された場合は t を返し、それ以外の値が渡された場合は nil を返すという関数です。 条件式の結果などを否定する場合に使うことができます。 例えば (not (<= a b)) とすれば、a が b より小さいか等しければ t を返しますが、それを not で否定するため結果が逆転します。

> (not nil)
T
> (not t)
NIL
> (not 10)
NIL

この結果を見れば、nil を渡されたときのみ t を返し、それ以外の値が渡された場合は常に nil を返すことがわかります。

and は、その後に指定された引数に nil が指定されていれば nil を返し、そうでなければ最後に指定された引数を返します。 例えば、複数の条件の結果がすべて t である場合は t、そうでなければ nil という条件を作る場合に使うことができるでしょう。

or は and の逆で、nil 以外の値が発見されればその値が結果となります。 全ての引数が nil の場合にのみ nil を返すのが or となります。

> (and 1 10 100)
100
> (and nil 10 100)
NIL
> (and t nil t)
NIL
> (and t)
T
> (or nil nil nil)
NIL
> (or t nil t)
T
> (or nil 10 nil 20)
10
> (or nil (not t) nil 100)
100

t 以外の値を含む and は最後の引数の値が返され、nil 以外の値を含む or は、最初の nil ではない値が結果となっていることがわかります。 実は and は nil が発見された時点で、or は nil 以外の値が発見された時点でその後の引数の評価を省略して結果を返します。

and や or の残りの式の評価を省略するという仕様は合理的なもので、通常はそれ自体が問題になることはありません。 and 条件に nil が発見されれば、その後の式がどのようなものであれ結果が nil であることが証明されています。 同様に nil 以外の値が発見された時点で or の結果は nil ではないことが証明されています。

しかし、残りの式の評価の中に変数の値を変更するような制御が含まれる場合、それが評価されないことを意識しなければ問題になることがあります。 次のコマンドを実行すると、残りの式が評価されていないことを証明することができます。

> (setq x 10)
10
> (and nil (= (setq x 100) 100))
NIL
> x
10
> (and t (= (setq x 100) 100))
T
> x
100

最初に変数 x を 10 で初期化しています。 その直後の and では最初の引数に nil を指定し、その後に setq 関数で x に値 100 を保存する式を指定しています。 (= (setq x 100) 100) では、(setq x 100) の結果は変数 x の値となるため、この式が評価された場合は t となります。 しかし、and の最初に引数が nil なので、この時点で and は残りの式を評価する必要がなくなり nil を返します。 x 変数を調べれば、値 100 が保存されておらず、初期の 10 のままであることが確認できます。 これによって、and は nil が発見された時点で残りの式の評価を省略するということが証明できました。



前のページへ戻る次のページへ