Common Lispの関数で色々な引数

Common Lispではいろいろな引数の渡し方がある。下の例は普通の関数の定義。

普通の関数

CL-USER> (defun foo (a b c d)
           (format t "a=~a b=~a c=~a d=~a" a b c d))
FOO
CL-USER> (foo 1 2)     ; 引数が足りない
; Evaluation aborted.
CL-USER> (foo 1 2 3)   ; これも引数が足りない
; Evaluation aborted.
CL-USER> (foo 1 2 3 4) ; 引数の数が妥当なので以下の出力が得られる
a=1 b=2 c=3 d=4NIL
CL-USER> 

オプショナルパラメータ

引数を省略するような時はオプショナルパラメータとして定義しておく。そうすると常に決められれた数の引数をとりたくないときは省略することが出来る。これにはオプションとしたい引数の前に「&optional」をつけて定義する。
下の例は、引数のa,bは必須で、c dに関してはオプションとなるので指定してもしなくてもよい。

CL-USER> (defun foo (a b &optional c d)
	   (format t "a=~a b=~a c=~a d=~a" a b c d))
FOO
CL-USER> (foo 1)      ; 必須パラメータのbがしていされてないので、これはエラー
; Evaluation aborted.
CL-USER> (foo 1 2 3)  ; オプショナルパラメータのcを省略した場合
a=1 b=2 c=3 d=NILNIL
CL-USER> (foo 1 2 3 4); 全部指定した場合
a=1 b=2 c=3 d=4NIL   

また、オプショナルパラメータにはデフォルト値も設定することが出来る。

CL-USER> (defun foo (a b &optional (c 99) (d a))
	   (format t "a=~a b=~a c=~a d=~a" a b c d))
FOO
CL-USER> (foo 1 2)
a=1 b=2 c=99 d=1NIL

(c 99)とすることで省略された場合99という値がcに設定され、(d a)とすることで、省略された場合dにはaに設定された値が入る。
(d c)としてオプショナルパラメータの値をデフォルトとすることも出来た。こんな感じ。

CL-USER> (defun foo (a b &optional (c 99) (d c))
	   (format t "a=~a b=~a c=~a d=~a" a b c d))
FOO
CL-USER> (foo 1 2 3)
a=1 b=2 c=3 d=3NIL

レストパラメータ

可変の引数を渡したいときは「&rest」を利用する。下の例では変数「values」引数として渡された値がリストとして設定されている。

CL-USER> (defun foo (&rest values)
	   (format t "~a" values))
FOO
CL-USER> (foo 1 2 3 4)
(1 2 3 4)NIL
CL-USER> (foo 1 2 3 4 5 6 7)
(1 2 3 4 5 6 7)NIL

キーワードパラメータ

これはよく使いそうな感じ。引数にキーと値のペアを指定して渡す事が出来る。「:key value」と言った感じ。

CL-USER> (defun foo (&key a b c d)
	   (format t "a=~a b=~a c=~a d=~a" a b c d))
FOO
CL-USER> (foo :a 10)
a=10 b=NIL c=NIL d=NILNIL
CL-USER> (foo :a 10 :b 20)
a=10 b=20 c=NIL d=NILNIL
CL-USER> (foo :d 40 :a 10 :c 30 :b 20)
a=10 b=20 c=30 d=40NIL

「:a 10」とすることで変数aに10を設定している。3つ目の例では引数の順番を変えているが「:キー 値」と変数に値を割り当てているため問題なく出力される。また、引数を省略しても問題ない。
またオプショナルパラメータと同様にデフォルト値も指定することが出来る。

CL-USER> (defun foo (&key (a 1) (b 2) (c 3) (d c))
	   (format t "a=~a b=~a c=~a d=~a" a b c d))
FOO
CL-USER> (foo)
a=1 b=2 c=3 d=3NIL

RubyJavaScriptでもこのパターンがよくでてくる気がするな。やっぱ、関数に何を渡しているか明示することで間違いも少なくなるし、変更にも強くなるからなんだろうな。

大事な事を忘れてた。てかこれを忘れそうだからメモしとこうと思ったのに。。
オプショナルパラメータとキーワードパラメータでは、「パラメータの名前 + supplied-p」という変数を定義すると、そのパラメータに値が設定された時はT(真)になり、設定されなかった場合はNIL(偽)となる。

CL-USER> (defun foo (&key (a 1) (b 2) (c 3) (d c d-supplied-p))
	   (format t "a=~a b=~a c=~a d=~a d-supplied-p:~a" a b c d d-supplied-p))
FOO
CL-USER> (foo :a 10 :b 20 :c 30)        ; dを省略した場合、d-supplied-pはNIL
a=10 b=20 c=30 d=30 d-supplied-p:NILNIL
CL-USER> (foo :a 10 :b 20 :c 30 :d 30)  ; dを明示的に設定した場合 d-supplied-pはT
a=10 b=20 c=30 d=30 d-supplied-p:TNIL

dにはデフォルト値と同じ30を指定しているけど、明示的にしていたってことでd-supplied-pはT(真)となる。
なかなか、おもしろい。