COMMON LISP JP > Archives > 2011/01/21

2011/01/21 09:30:45 UTCShinnya
#
こんにちは。
#
すいません誤爆です。
お聞きしたいことがあるのですが、Common Lispの関数定義でオプショナルなキーワード引数を指定したいんですけど、これって出来ますか? 試行錯誤してみた結果無理だと思ってるんですがどうでしょう。
2011/01/21 09:35:26 UTCmkamotsu
#
(defun foo (&key (a 0))
#
body)
#
なかんじですか?
2011/01/21 09:39:59 UTCShinnya
#
すみません、僕の説明が悪かったです。
(foo "hello") と (foo :a "hello") の両方が使えるようにしたいということです。
なので、(defun foo (&key (a 0)) body) はちょっとやりたいことと違います。
2011/01/21 09:46:46 UTCmkamotsu
#
オプショナル引数を持つ関数は,オプショナル引数を全部使わないとキーワードが使えないので無理っぽい,という話ですね.
2011/01/21 09:50:52 UTCShinnya
#
んー、やっぱ無理ですよね。ありがとうございました。
2011/01/21 11:08:49 UTCbowbow99
#
(defun foo-1 (a b) ...)
#
としておいて
#
(defun foo (&rest args)
#
(apply 'foo-1 (if (keywordp (car args))
#
(loop for k in '(:a :b) collect (getf args k))
#
args)))
#
と無理矢理やってみました。
#
あ、foo-1 は (defun foo-1 (&optional a b) ...) にしないとダメすね。
2011/01/21 12:12:23 UTCshiro
#
&optional a b と &key a b を両方受け付けるようにした時に、(foo :a "hello") は「aというキーワード引数に"hello"が渡された」か「aというoptional引数に:aが、bというoptional引数に"hello"が渡された」かを区別できないですね。「キーワードでマッチしたらそっち優先」というルールを設ければ別ですが、それができるかどうかはアプリに依存するでしょう。
2011/01/21 12:22:07 UTCShinnya
#
やはり完全には無理みたいですね。。
#
bowbow99さんの方法だと1引数だとうまく動くように思います。
#
(defmacro %defun (name args &body body)
  (let ((func (gensym)))
    `(progn
       (defun ,func (&optional ,@args)
         ,@body)
       (defun ,name (&rest args)
         (apply ',func
                (if (keywordp (car args))
                    (loop
                       for k in ',(mapcar
                                   #'(lambda (arg)
                                       (if (listp arg)
                                           (intern (symbol-name (car arg))
                                                   "KEYWORD")
                                           (intern (symbol-name arg)
                                                   "KEYWORD")))
                                              args)
                       collect (getf args k))
                    args))))))
#
CL-USER> (defun$ foo ((a "hello")) (print a))
FOO
CL-USER> (foo)

"hello" 
"hello"
CL-USER> (foo "world")

"world" 
"world"
CL-USER> (foo :a "world")

"world" 
"world"
CL-USER>
2011/01/21 12:29:34 UTCShinnya
#
あ、貼るものを間違えました。defun$ のところは %defun です。
2011/01/21 12:54:30 UTCbowbow99
#
最初の引数がキーワードだったら全部キーワード引数と見なすようになっちゃってるので (foo :a 1 :b 2) とかならいけるはず。(foo 1 :b 2) とか (foo :a 1 2) とかはダメですね。
2011/01/21 13:01:32 UTCshiro
#
APIを変更したけれど以前のAPIも当面受け付けたい、なんて場合には、多少の制限があっても構わないので、そんな感じで両方サポートすることはありますね。
2011/01/21 16:51:41 UTCShinnya
#
なるほど。