nptclのブログ

Common Lisp処理系nptの開発メモです。https://github.com/nptcl/npt

引数&keyの内容を指定しない場合を考える

例えば

(defmethod aaa (value &key)
  ...)

のように&keyだけを指定して、その後の変数を記載しない場合があります。
全くの無意味のように見えますが、下記2つの場合において意味があります。

  • 続けて&allow-other-keysを指定した場合
  • defmethoddefgenericでエラー回避のため

それぞれ説明します。

続けて&allow-other-keysを指定した場合

&allow-other-keysの場合は簡単に説明できます。
例えば下記の通り。

(defun aaa (value &key &allow-other-keys)
  ...)

このようにすることで、関数aaaの引数にkey-valueの形を要求させることができます。
関数aaaを呼び出すことを考えましょう。
例えば、下記の場合は問題ないです。

(aaa 100 :aaa 200 'bbb 300)

しかし、key-valueの形になっていない場合はエラーです。

(aaa 100 :aaa 200 'bbb)
  →★エラー、valueが足りない

(aaa 100 :aaa 200 300 400)
  →★エラーの可能性あり、keyは本来symbolじゃないとダメ

関数aaaでは、引数を指定した所でただ内容を切り捨てています。 そうではなく引数&keyに加えて、&restだったり マクロなら&whole&bodyと組み合わせるなら、 もっとちゃんとした使い方ができると思います。

(defun aaa (value &rest args &key &allow-other-keys)
  ...)

この例では、key-valueの形を要求していることに加えて、 その内容を&restにてargsに格納しています。

defmethoddefgenericでエラー回避のため

ではdefmethoddefgenericの方はどうでしょうか。

defgenericdefmethodには、lambda-listを設定するときに、 守らなければいけない条件がいくつかあります。
そのうちの一部を下記に示します。

  • defgeneric&rest&keyがあるなら、defmethod&rest&keyが必要。
  • defmethod&rest&keyがあるなら、defgeneric&rest&keyが必要。

では下記の例を見てみましょう。

(defgeneric aaa (value &rest args))

defgenericの引数には&restが含まれているので、 defmethodでは&rest&keyが必要になります。
続けて次の例を見ていきます。

(defmethod aaa (value &key hello)
  ...)

上記のmethodでは&keyがあるので問題なしです。
次はどうでしょうか。

(defmethod aaa (value)
  ...)

上記はエラーです。 defgeneric&restがあるのに、methodには&rest&keyもありません。

では次の例はどうでしょうか。

(defmethod aaa (value &key)
  ...)

面白いことに、&keyがあるから問題ないとみなされます。

(defmethod aaa (value)  ;; ★エラー
  ...)
(defmethod aaa (value &key)  ;; ★問題なし
  ...)

上記2例は、受け取ることができる引数は全く同じであるにもかかわらず、 下の方だけが合法で、lambda-listのエラーチェックを回避できるのです。