ド素人が始めるCommon Lisp 7. 関数を定義する defun

Common Lisp ド素人

今回も引き続き"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は引数が評価されないマクロ
  • クォートの使い方に注意が必要(特に私のようなド素人は)

コメント

タイトルとURLをコピーしました