#こんにちは。
#すいません誤爆です。
お聞きしたいことがあるのですが、Common Lispの関数定義でオプショナルなキーワード引数を指定したいんですけど、これって出来ますか? 試行錯誤してみた結果無理だと思ってるんですがどうでしょう。
#(defun foo (&key (a 0))
#body)
#なかんじですか?
#すみません、僕の説明が悪かったです。
(foo "hello") と (foo :a "hello") の両方が使えるようにしたいということです。
なので、(defun foo (&key (a 0)) body) はちょっとやりたいことと違います。
#オプショナル引数を持つ関数は,オプショナル引数を全部使わないとキーワードが使えないので無理っぽい,という話ですね.
#んー、やっぱ無理ですよね。ありがとうございました。
#(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) ...) にしないとダメすね。
#&optional a b と &key a b を両方受け付けるようにした時に、(foo :a "hello") は「aというキーワード引数に"hello"が渡された」か「aというoptional引数に:aが、bというoptional引数に"hello"が渡された」かを区別できないですね。「キーワードでマッチしたらそっち優先」というルールを設ければ別ですが、それができるかどうかはアプリに依存するでしょう。
#やはり完全には無理みたいですね。。
#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>
#あ、貼るものを間違えました。defun$ のところは %defun です。
#最初の引数がキーワードだったら全部キーワード引数と見なすようになっちゃってるので (foo :a 1 :b 2) とかならいけるはず。(foo 1 :b 2) とか (foo :a 1 2) とかはダメですね。
#APIを変更したけれど以前のAPIも当面受け付けたい、なんて場合には、多少の制限があっても構わないので、そんな感じで両方サポートすることはありますね。
#なるほど。