今回も引き続き"Common Lisp: A Gentle Introduction to Symbolic Computation"を読んで自分の理解をまとめていく。
前回、Lispのコードとして表記したものを評価(EVALuatioin)してもらえるようになりました。もともと処理系が持っている関数を評価して試してみましたが、今回は自分で関数を定義していきます。
Contents
関数定義の仕方
関数定義にはdefunを使う
実は「ド素人が始めるCommon Lisp」第一回ですでに使っています。
(defun hello-world () (format t "Hello, World!~%"))
このようにdefun
の引数として定義する関数名、引数リスト、関数本体を書けばOK。今回の例の場合は関数名hello-world
、引数リスト()
、関数本体(format t "Hello, World!~%")
です。
defunの引数は評価されない
前回Common Lispの評価について書きました。
その中でリストの中身は評価される、引数も評価されてから関数に渡されるということを学びました。ただdefun
についてはそれが該当しません。マクロや特殊形式(special form)などはそういうものだそうだ。defun
はマクロ。当たり前だがdefun
で定義した関数を使用する際の引数は評価される。
defunを使ってみる
既にhello-world
は作ってみたが他にも試してみよう。
まずはよくある階乗計算
(defun fact (n) (if (= n 1) 1 (* n (fact (- n 1)))))
何も工夫もない再帰で書いてあるのでスタック積まれ放題ですね。とりあえずn=5000
くらいは問題なく答えが出てた。
次はこれもよくあるフィボナッチ数列
(defun fib (n) (cond ((= n 1) 1) ((= n 2) 1) (t (+ (fib (- n 1)) (fib (- n 2))))))
この書き方もスタック積みまくりなので大きい数字には使えません。
どちらの例でもif
とかcond
とか使っていますがこれらの条件分岐は次回もう少し詳しくみていきます。
defun よくある失敗
引数が数値だとわかりにくいがシンボルの場合にはクォートに注意しないといけない。例えば以下のwhat-is-this
関数
(defun what-is-this (obj) (list 'This 'is 'a obj)) (what-is-this 'pen) ; -> (THIS IS A PEN)
与えられたシンボルに対して(THIS IS A obj)というリストを返す関数を作ってみる。
上で述べたようにdefun
で定義しているwhat-is-this
の引数リスト(obj)
は評価されないのでクォートする必要はない。クォートしてしまうとdefun
評価時に以下のようなエラーとなる。
(defun what-is-this ('obj) ; wrong quote (list 'This 'is 'a obj)) ; in: DEFUN WHAT-IS-THIS ; (SB-INT:NAMED-LAMBDA WHAT-IS-THIS ; ('OBJ) ; (BLOCK WHAT-IS-THIS (LIST 'THIS 'IS 'A OBJ))) ; ; caught ERROR: ; Required argument is not a symbol: 'OBJ
次の間違いは定義した関数のbodyで引数をクォートしてしまうケース。
(defun what-is-this (obj) (list 'This 'is 'a 'obj)) ; wrong quote (what-is-this 'pen) ; -> (THIS IS A OBJ)
defun
評価時にもOBJ
が使われてないよというwarningはでます。無視してその関数を使うと上に示したようにwhat-is-this
関数の引数に何を与えても(THIS IS A OBJ)
が返ってきてしまう。
次の間違いは必要なクォートを忘れるケース。
(defun what-is-this (obj) (list This is a obj)) ; forget to quote (what-is-this 'pen) ; -> Unbound variable error
このケースもdefun
評価時にwarningがでます。使用すると上記の通りUnbounded variableエラーです。
ただし事前にTHIS, IS, A
が束縛されていればエラーとはなりません。だからこそ定義時はwarningだけなのだろう。
(setf this "THIS" is "IS" a "A") (what-is-this 'pen) ; -> ("THIS" "IS" "A" pen)
まとめ
defun
を使用して関数定義できる- (defun 関数名 引数リスト 関数本体)という形で記述する
defun
は引数が評価されないマクロ- クォートの使い方に注意が必要(特に私のようなド素人は)
コメント