プログラミング原人の進化ログ

プログラミング原人の進化論

オレ プログラミング ベンキョウ スル。マナンダ コト カク。

【Common Lisp】条件分岐:if, when, unless, cond

Common Lispの条件分岐についてのメモです。条件分岐のためのコマンドと、それらを使うのに必要な基本的な演算子や関数をまとめておきます。

真偽値

common lispでは、空リストが唯一の偽値です。それ以外は全て真となります。
なお、'(), (), nil, 'nilは空リストとして扱われます。

条件分岐のコマンド

if

(if 条件
    真の場合の処理
    偽の場合の処理)

例:

CL-USER> (if (= (+ 1 2) 3)
             'true
             'false)
TRUE
CL-USER> (if (= (+ 1 2) 4)
             'true
             'false)
FALSE


ifコマンドは一つの式しか評価しないので、もし2つ以上の式を処理させる必要があるのであればprognコマンドを使います。

CL-USER> (defvar *number-was-odd* nil)
*NUMBER-WAS-ODD*
CL-USER> (if (oddp 5)
             (progn (setf *number-was-odd* t)
                    'odd-number)
             'even-number)
ODD-NUMBER
CL-USER> *number-was-odd*
T

when, unless

複数の処理を行うときは、whenやunlessを使うと便利です。whenは条件が真の時、unlessは条件が偽の時にブロックを実行します。
条件が真と偽の場合両方を処理する必要があるときは if ですが、そうでなければこちらの方が簡単かも。

CL-USER> (defvar *number-is-odd* nil)
*NUMBER-IS-ODD*
CL-USER> (when (oddp 5)
           (setf *number-is-odd* t)
           'odd-number)
ODD-NUMBER
CL-USER> *number-is-odd*
T
CL-USER> (unless (oddp 4)
           (setf *number-is-odd* nil)
           'even-number)
EVEN-NUMBER
CL-USER> *number-is-odd*
NIL

cond

condコマンドは、複数の条件分岐を行うことができ、それぞれで複数の処理を行うことができます。

CL-USER> (defvar *arch-enemy* nil)
*ARCH-ENEMY*
CL-USER> (defun pudding-eater (person)
           (cond ((eq person 'henry) (setf *arch-enemy* 'stupid-lisp-alien)
                  '(curse you lisp alien - you ate my pudding))
                 ((eq person 'johnny) (setf *arch-enemy* 'useless-old-jonny)
                  '(i hope you choked on my pudding johnny))
                 (t '(why you eat my pudding stranger ?))))
PUDDING-EATER
CL-USER> (pudding-eater 'johnny)
(I HOPE YOU CHOKED ON MY PUDDING JOHNNY)
CL-USER> *arch-enemy*
USELESS-OLD-JONNY
CL-USER> (pudding-eater 'bob)
(WHY YOU EAT MY PUDDING STRANGER ?)

最後の条件がtになっていますが、これはcondでは上から順に条件が評価されて最初に真であったものが実行されるためです。いずれかの分岐が必ず実行されるようになっています。他言語のif-elseみたいなもんです。

case

例えばC言語におけるswhich-caseと似ています。条件式が真となる最初の分岐が実行されます。上の関数は次のように書き直すことができます。

CL-USER> (defun pudding-eater (person)
           (case person
             ((henry) (setf *arch-enemy* 'stupid-lisp-alien)
              '(curse you lisp alien - you ate my pudding))
             ((johnny) (setf *arch-enemy* 'useless-old-johnny)
              '(i hope you choked on my pudding johnny))
             (otherwise '(why you eat my pudding stranger ?))))
PUDDING-EATER
CL-USER> (pudding-eater 'johnny)
(I HOPE YOU CHOKED ON MY PUDDING JOHNNY)
CL-USER> *arch-enemy*
USELESS-OLD-JOHNNY

論理演算子

and, or

CL-USER> (and (oddp 5) (oddp 7) (oddp 9))
T
CL-USER> (or (oddp 4) (oddp 7) (oddp 9))
T

andは全ての式が真であれば真を、一つでも偽の式があれば偽を返します。orは少なくとも1つの式が真であれば真を返し、全ての式が偽であれば偽を返します。なお、無駄を省くため、andは偽の式を発見したらそこで評価を終わりにし、orは真の式を発見したらそれ以降の式は評価しません。

not

真偽を逆転させます。

CL-USER> (not nil)
T
CL-USER> (not t)
NIL
CL-USER> (not 1)
NIL
CL-USER> (not '(1 2 3))
NIL
CL-USER> (not '(nil))
NIL
CL-USER> (not 'a)
NIL

基本的な比較関数

比較関数には色々な種類があるみたいです。基本的にはequalで事足りるようですが、シンボル同士を比較するときはeqを使った方が良いらしいです。eqはシンボル同士の比較にしか使えないが、非常に高速とのこと。

例:

CL-USER> (eq 'apple 'apple)
T
CL-USER> (equal 'apple 'apple)
T
CL-USER> (equal (list 1 2 3) '(1 2 3))
T
CL-USER> (equal '(1 2 3) (cons 1 (cons 2 (cons 3 ()))))
T
CL-USER> (equal 5 5)
T
CL-USER> (equal 2.5 2.5)
T
CL-USER> (equal "foo" "foo")
T
CL-USER> (equal #\a #\a)
T

他にも eql, = などいくつも関数があります。マスターしたらまとめておきたいです。

参考

コンラッドバルスキ(2013)「Land of Lisp」 川合史郎訳 オライリー・ジャパン