我正在尝试编写包含其他函数的函数,但我不确定如何在维护合理的lambda列表的同时正确传递参数。
E.g。如果我有一个功能
(defun f (x &key y z) ...)
我想写点像
(defun g (x &key y z)
(h (f x :y y :z z)))
这是不能令人满意的,因为我想从f
调用g
并调用g
的确切参数,但这不会发生(例如,我不想提供f
的关键字参数,这些参数未被调用者提供给g
。
我最初写的是:
(defun g (&rest f-args)
(apply #'f f-args))
这就是我想要的效果,但是g
的lambda列表现在非常神秘,我不得不导航到f
以查看参数应该是什么。
我确实想出了一个解决方案(而且它大部分都令人满意,所以我将其作为答案发布),但我需要明确每一个关键参数,以及大型lambda列表(例如,如果我想要包装{ {3}}),这将是一种痛苦。我希望也许有更好的方法。
答案 0 :(得分:3)
您可以通过从另一个函数复制lambda列表来编写一个定义函数的宏。问题在于,没有标准方法来获取lambda列表,但对于SBCL,您可以使用SB-INTROSPECT:FUNCTION-LAMBDA-LIST
(尽管它不能与(declaim (optimize (debug 0)))
一起使用)。您可以尝试阅读Swank源代码,了解它如何获得各种实现的lambda列表。
(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
由于代码没有显示变量定义,因此有点难看。更复杂的解决方案可能是做
之类的事情;; 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 _))
使用给定参数调用函数_
替换包装器中的F
。您仍然需要记住参数变量存在并且可能与您自己定义的变量冲突。
将所有参数传递给函数,无论它们是否被赋予。这可能会破坏一个行为不同的函数,具体取决于是否提供了参数。您可以使用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)
答案 1 :(得分:2)
我想出了这个:
(defun g (x &rest f-keys &key y z)
(declare (ignorable y z))
(apply #'f x f-keys))
对于小型lambda列表来说这很棒,但我希望我能做得更好 除非我明确地输入默认值,否则我也看不到默认值。