&rest の考察と関数パラメータのまとめ
Lispで関数を定義する際のパラメータに付与するキーワードとして、&key、&optionalというのをやってみたが、他にも&restというのがある。
これは、試してみたがどうやら「個数を限定できない」ということを意味するパラメータのようだ。たとえば、
(list 0 1 2 3 4)
という関数は、パラメータがいくつあってもそれら全てを連結して一つのリストにする。実際、これを定義するとしても、呼び出されるその瞬間まで引数がいくつになるかわからない、という問題がある。こういうタイプの関数を定義する際に使用するキーワードだ。Javaにはこういうパラメータはない。Cだと、...で定義する引数が近そうだ。
では、これらを組み合わせるとどのような動作になるのか。
? (defun param-keyword-testfunc (&rest rest &optional x y) (list x y rest)) > Error: While compiling PARAM-KEYWORD-TESTFUNC : > Bad lambda list : (&REST REST &OPTIONAL X Y), in process listener(1). > Type :GO to continue, :POP to abort, :R for a list of available restarts. > If continued: continue compilation ignoring this form > Type :? for other options. 1 > q ? (defun param-keyword-testfunc (&optional x y &rest rest) (list x y rest)) PARAM-KEYWORD-TESTFUNC ? (param-keyword-testfunc 50 60 10 20 30 40) (50 60 (10 20 30 40)) ? (param-keyword-testfunc 50) (50 NIL NIL) ?
&restと&optionalを組み合わせる場合は、
- &optional,&restという順番にする必要がある
- &restの内容は、一つのリストにまとめられて処理される
- 引数の数が不足していれば、NILが指定されたものとして処理される
ということがわかった。
? (defun param-keyword-testfunc (&key x y &rest rest) (list x y rest)) > Error: While compiling PARAM-KEYWORD-TESTFUNC : > Bad lambda list : (&KEY X Y &REST REST), in process listener(1). > Type :GO to continue, :POP to abort, :R for a list of available restarts. > If continued: continue compilation ignoring this form > Type :? for other options. 1 > q ? (defun param-keyword-testfunc (&rest rest &key x y) (list x y rest)) PARAM-KEYWORD-TESTFUNC ? (param-keyword-testfunc 10 20 30) > Error: Incorrect keyword arguments in (10 20 30) . > While executing: CCL::TOPLEVEL-EVAL, in process listener(1). > Type :POP to abort, :R for a list of available restarts. > Type :? for other options. 1 > q ? (param-keyword-testfunc :x 10 :y 20 30 40 50) > Error: Incorrect keyword arguments in (:X 10 :Y 20 30 40 50) . > While executing: CCL::TOPLEVEL-EVAL, in process listener(1). > Type :POP to abort, :R for a list of available restarts. > Type :? for other options. 1 > q ? (param-keyword-testfunc :x 10 :y 20) (10 20 (:X 10 :Y 20)) ? (param-keyword-testfunc :x 10) (10 NIL (:X 10)) ? (param-keyword-testfunc :y 20) (NIL 20 (:Y 20)) ? (param-keyword-testfunc 10) > Error: Incorrect keyword arguments in (10) . > While executing: CCL::TOPLEVEL-EVAL, in process listener(1). > Type :POP to abort, :R for a list of available restarts. > Type :? for other options. 1 > q ? (param-keyword-testfunc :x 10 :y 20 30) > Error: Incorrect keyword arguments in (:X 10 :Y 20 30) . > While executing: CCL::TOPLEVEL-EVAL, in process listener(1). > Type :POP to abort, :R for a list of available restarts. > Type :? for other options. 1 > q ?
&restと&keyを組み合わせる場合は、
- &rest,&keyという順番でなければならない。
- &key以外のパラメータは指定できない
- &keyのものは、&keyのものとして処理され、&restには&keyの名前自体と値の組み合わせ全体が格納される
ということがわかる。この結果から、&optionalの意義がわかってきた。&key以外の引数が必要になるかもしれず、ならないかもしれず・・・という場合に対処するには、&keyでなく&optionalを使用しないといけない。呼出時に渡される引数の個数が定まっておらず、その中のいくつかは必須という場合は&keyは使用できない、ということになる。便利そうなのだが。
最後は、&keyと&optionalの組み合わせ。これは・・・上記の結果から推測すると矛盾がありそう。&keyは、&keyで定義した引数以外があるとNGだから、&optionalパラメータをうまく使用できないのではないかと思われる。
? (defun param-keyword-testfunc (&key x y &optional a b) (list x y a b)) > Error: While compiling PARAM-KEYWORD-TESTFUNC : > Bad lambda list : (&KEY X Y &OPTIONAL A B), in process listener(1). > Type :GO to continue, :POP to abort, :R for a list of available restarts. > If continued: continue compilation ignoring this form > Type :? for other options. 1 > q ? (defun param-keyword-testfunc (&optional a b c &key x y z) (list a b c x y z)) PARAM-KEYWORD-TESTFUNC ? (param-keyword-testfunc 10 20 30 :x 100 :y 200 :z 300) (10 20 30 100 200 300) ? (param-keyword-testfunc 10 :x 100 :y 200 :z 300) (10 :X 100 NIL 200 300) ?
この組み合わせの場合は、
- 予想に反して組み合わせは可能だが、順番は&optional,&keyとなる。
- &optionalの値にNILになるものがあるとおかしな結果になる。
ということになるか。実際に使用する場合は、&restと&key、または&optionalのどちらかを組み合わせて使用する、ということになりそうだ。