[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [bep] learning lisp: if, and, cond
- To: bep@argv.org
- Subject: Re: [bep] learning lisp: if, and, cond
- From: TAKAHASHI Naoto <ntakahas@m17n.org>
- Date: Tue, 19 Jun 2001 12:58:36 +0900 (JST)
- Delivered-To: mailing list bep@argv.org
- Mailing-List: contact bep-help@argv.org; run by ezmlm
- User-Agent: SEMI/1.14.3 (Ushinoya) FLIM/1.14.2 (Yagi-Nishiguchi) APEL/10.2 Emacs/21.0.104 (sparc-sun-solaris2.8) MULE/5.0 (SAKAKI)
n高橋です。
Reiko TAKAHASHI (高橋玲子)writes:
> } 練習問題:
> } 前出の fact に変更をほどこし、引数が1以上の整数でなければ nil を返すよ
> } うにせよ。
> 考えてみました。
> (defun fact (n)
> (if (and (integerp n) (>= n 1))
> (if (= n 1)
> 1
> (* n (fact (- n 1))))))
> でしょうか?
はい、いいようですね。もしかして初 defun ですか?
だとすると、これで今日から lisp プログラマですね。
> (if ...)の中身について、条件式を評価した結果が t の場合どうするかのS式さ
> えあれば、nil の分のS式はべつになくてもいいんだ、ということが、教えていた
> だいていたはずなのに初め思いつけず、「うわっ、nil の分はどうしよう……」
> と最初少し悩みました。
上の書き方でいいんですが、もし明示的に nil を返したいのなら、そのまま
nil と書けばいいんですよ。もちろん nil もS式です。たとえば
(defun meaningless (arg) nil)
とすると、何を引数に与えても nil を返す無意味関数 meaningless が定義で
きます。
もし仮にR高橋版 fact を、明示的に nil 返すように書き変えるなら、
(defun fact(n)
(if (and (integerp n) (>= n 1))
(if (= n 1)
1
(* n (fact (- n 1))))
nil))
ですね。
さて、実はここの and の中の integerp と >= の順序は非常に重要です。も
し >= を先にして (and (>= n 1) (integerp n)) としてしまうと、(fact 'x)
とか (fact "abc") とかやったときエラーになってしまいます。>= の引数は
どちらも数値でなければならないからです。
and の一般形は (and 条件1 条件2 条件3 ...) という形をしていますが、
条件1, 条件2, ... を全部評価してからそれらが全部真かどうか調べるのでは
ありません。条件xを前から順番に評価して行き、どれか1つが nil となった
時点で残りの条件は無視して即座に nil を返します。ですから、
(setq x 5)
(and nil (setq x 6))
とした後でxの値を調べても5のままになっています。
> 上のLISPを*scratch*でC-jしてから、(fact ...) にいろいろな数を入れて遊ん
> でみました。
> 私のところでは、n の値が11を超えてしまうと、返ってくる値が負の数になっ
> たり、正の数でもへんな値になったりするみたいなのですが……(DOS版Emacsで
> す(^^;;;))。
Emacs Lisp が扱える整数の範囲には限りがあり、それを超えると変になりま
す。この範囲はプログラミング言語としては狭い方ですが、もっと大きな数が
使いたいようになる頃には、どうすればいいかもわかってくると思います。
> } もちろん if を重ねることもできます。なにしろ (if ...) もS式のひとつで
> } すから。なお、複雑な条件判定をするときには、if より便利な cond を使う
> } ことがよくあります。でも cond の紹介は上の練習問題ができた後ですね。
cond の一般形は次のような形をしています。
(cond
(条件1 S式1の1 S式1の2 S式1の3 ...)
(条件2 S式2の1 S式2の2 S式2の3 ...)
(条件3 S式3の1 S式3の2 S式3の3 ...)
...
)
「条件x」を上から順に見て行き、評価結果が真になったら
「S式xの1」「S式xの2」「S式xの3」…を順番に評価して最後の「S式xの?」
の値を返します。
and の場合と同様、残りの「条件x+1」「条件x+2」…は調べさえしません。
では cond を使って fact を書き直してみましょう。
(defun fact (n)
(cond
((not (integerp n)) nil)
((< n 1) nil)
((= n 1) 1)
(t (* n (fact (1- n))))))
なんとなく想像つきますか?
「条件1」は (not (integerp n)) で、「S式1の1」は nil
意味は、もしnが整数でなかったら値は nil、です。
「条件2」は (< n 1) で、「S式2の1」はやっぱり nil
意味は、もしnが1よりも小さかったら値は nil、です。
「条件3」は (= n 1) で、「S式3の1」は1
意味は、もしnが1だったら値は1、です。
「条件4」は t で、「S式4の1」は (* n (fact (1- n)))
意味は、無条件で値は n かける (fact n引く1)、です。
いずれの場合も場合も「S式xの2」以降が存在しないので、「S式xの1」の値が
全体の値になります。
最後の「条件4」の t がちょっとわかりにくいかもしれません。上で書いたよ
うに、cond は条件xが真になったところでそれ以降の条件のチェックをとばし
ます。ということは、もし「条件4」のところまで来たのならば、
・nは整数でなくなく(つまり整数)、
・かつ1より小さくなく(つまり1以上)、
・しかも1でない、
ということになります。わかりやすく言うと、nは1よりも大きい整数だという
ことになります。nが1よりも大きな整数であれば、そのときの答はnの値にか
かわらず (* n (fact (1- n))) です。ですから「条件4」を t (つまり真)
としておいて、常に (* n (fact (1- n))) を返すようにしているわけです。
cond を書くときは以上のように、まず例外的な場合を取り出し、最後に「そ
れ以外なら」という意味で t という条件を置くのが普通です。もちろん Lisp
は nil 以外をすべて真ととらえますから、t の変わりに 1 とか 'a とか
"hello" とか書いてもいいんですが、わかりやすさのため t を使うのが習慣
です。
なお、cond の条件の最後が t でなく、かつすべての条件が nil となった場
合、cond 自身の値も nil となります。
--
TAKAHASHI Naoto
ntakahas@...
http://www.m17n.org/ntakahas/