Shammer's Philosophy

My private adversaria

&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のどちらかを組み合わせて使用する、ということになりそうだ。