ラムダリストを別の関数からコピーして関数を定義するマクロを書くことができます。問題はラムダリストを取得する標準的な方法がないことですが、SBCLの場合はSB-INTROSPECT:FUNCTION-LAMBDA-LIST
を使用できます(ただし、(declaim (optimize (debug 0)))
では機能しません)。スワンクのソースコードを読んで、さまざまな実装のラムダリストを取得する方法を調べることができます。コードは変数定義を示していないので、少し醜い
(defmacro define-wrapper (name lambda-source &body body)
`(defun ,name ,(sb-introspect:function-lambda-list lambda-source)
,@body))
(defun f (x &key (y 3) (z 4))
(+ x y z))
(define-wrapper g f
(* 2 (f x :y y :z z)))
(f 2) ;=> 9
(g 2) ;=> 18
。もう少し複雑なソリューションは、ラッパーで_
が与えられた引数を持つ関数F
への呼び出しに置き換えられ
;; Requires Alexandria.
(defmacro define-wrapper (name lambda-source &body body)
(let ((lambda-list (sb-introspect:function-lambda-list lambda-source)))
(multiple-value-bind (required optional rest keywords)
(alexandria:parse-ordinary-lambda-list lambda-list)
(declare (ignore rest))
`(defun ,name ,lambda-list
,@(sublis `((_ . (,lambda-source ,@(loop for r in required collect r)
,@(loop for (name init suppliedp)
in optional collect name)
,@(loop for ((k-name name) init suppliedp)
in keywords
append (list k-name name)))))
body)))))
(defun f (x &key (y 3) (z 4))
(+ x y z))
(define-wrapper g f
(* 2 _))
のような何かをするかもしれません。引数変数が存在し、自分で定義した変数と競合する可能性があることを覚えておく必要があります。
これは、与えられたかどうかにかかわらず、関数にすべての引数を渡します。それは、引数が与えられたかどうかに応じて異なる振る舞いをする関数を混乱させる可能性があります。 APPLY
を使用して回避することはできますが、もう少し複雑です。
(defmacro define-wrapper (name lambda-source &body body)
(let ((lambda-list (sb-introspect:function-lambda-list lambda-source)))
(alexandria:with-gensyms (deparsed-arglist-sym
key-sym val-sym suppliedp-sym)
(multiple-value-bind (required optional rest keywords)
(alexandria:parse-ordinary-lambda-list lambda-list)
(declare (ignore rest))
(multiple-value-bind (body declarations docstring)
(alexandria:parse-body body :documentation t)
`(defun ,name ,lambda-list
,@(when docstring (list docstring))
,@declarations
(let ((,deparsed-arglist-sym
(nconc (loop for ,val-sym in (list ,@required) collect ,val-sym)
(loop for (,val-sym . ,suppliedp-sym)
in (list ,@(loop for (name init suppliedp)
in optional
collect (list 'cons name
(or suppliedp t))))
when ,suppliedp-sym collect ,val-sym)
(loop for (,key-sym ,val-sym ,suppliedp-sym)
in (list ,@(loop for ((kname name) init suppliedp)
in keywords
collect (list 'list kname name
(or suppliedp t))))
when ,suppliedp-sym append (list ,key-sym ,val-sym)))))
,@(sublis `((_ . (apply #',lambda-source ,deparsed-arglist-sym)))
body))))))))
(define-wrapper bar drakma:http-request
"Return the length of a response to http-request."
;; HTTP-REQUEST has some &aux variables.
(declare (ignore drakma::unparsed-uri
drakma::args))
(length _))
(bar "http://www.google.com") ;=> 11400 (14 bits, #x2C88)
キーワードの引数には、次のような形式を使用できます。 '(y nil y-p)'引数は引数が与えられていれば 't'、省略された場合は' nil'となります。これからラムダリストを再構成する必要があります。 – mobiuseng