一开始,我认为它很简单,因为它是Common Lisp,拥有所有语言中最强大的宏功能。但现在,8个小时后,我的背面出现了逗号和准引号,我离解决方案不远了。
我需要一个能产生3个函数的宏。其签名如下:
(defmacro remote (name arg-names &body body) ...)
它应该生成(我在下面的文本中使用"foo"作为名称参数(:
(defun foo (,arg-names) ,@body )
(只是foo的直接实现(,可以作为(foo arg1 arg2 ... )
调用。CCD_ 3,其可以被调用为CCD_。
(foo arg1 arg2 ...)
(defun remote-defun-foo () ...
,当像这样调用时,(remote-defun-foo)
返回函数(defun foo (,args) ,@body)
的引用定义。
我找到了1的解决方案。但我对如何写2一无所知。和3。这是我迄今为止所拥有的。
(defun make-remote-name (name)
(read-from-string (format nil "remote-~a" name)))
(defun make-definition-name (name)
(read-from-string (format nil "remote-defun-~a" name)))
(defmacro remoted (name arg-names &body body)
`(progn
(defun ,name ,arg-names ,@body) ;; 1.
))
我不确定,但是2。和3。我似乎需要一些嵌套的准引号或其他我以前从未使用过的技巧。
如上所述,这并不难:
(defmacro define-remote (name (&rest args) &body decls/forms)
(let ((remote-name (intern (format nil "REMOTE-~A" (symbol-name name))))
(remote-defun-name (intern (format nil "REMOTE-DEFUN-~A"
(symbol-name name))))
(def `(defun ,name (,@args) ,@decls/forms)))
`(progn
,def
(defun ,remote-name (,@args) `(,',name ,,@args))
(defun ,remote-defun-name ()
',def)
',name)))
然后
> (macroexpand '(define-remote foo (x) x))
(progn
(defun foo (x) x)
(defun remote-foo (x) `(foo ,x))
(defun remote-defun-foo () '(defun foo (x) x))
'foo)
t
棘手的部分(这个答案的早期版本错了,因为我误读了这个问题(是第二种形式,你想使用你创建的函数中的参数创建一个带引号的调用,所以你必须稍微摆弄一下嵌套的反引号。
我发现有助于理解这一点的一种方法是根据list
重写第二种形式,然后记住
(list x y)
与相同
`(,x ,y)
(事实证明,我不知道如何在降价时将反向报价转换为内联代码。(
然而,所有这些都有一个令人讨厌的潜在问题:资料片中的第二种形式通常是错误的。考虑一下:
> (macroexpand '(define-remote foo (&key (x 1)) x))
(progn
(defun foo (&key (x 1)) x)
(defun remote-foo (&key (x 1)) `(foo ,&key ,(x 1)))
(defun remote-defun-foo () '(defun foo (&key (x 1)) x))
'foo)
t
remote-foo
表单是错误的,因为它在lambda列表关键字中拼接。几乎可以肯定的是,扩展应该是
(progn
(defun foo (&key (x 1)) x)
(defun remote-foo (&key (x 1)) `(foo :x ,x))
(defun remote-defun-foo () '(defun foo (&key (x 1)) x))
'foo)
我认为,要做到这一点并不简单:你需要一个可以获取lambda列表并将其转换为与其对应的arglist的东西,我不认为这在CL中是一个预先绑定的东西。我确信它是有人写的,但我只是不知道在哪里。
举个例子说明为什么这并不简单,考虑一下这个
(define-remote foo (&key (x 1 xp) ...))
现在remote-foo
可能需要
(defun remote-foo (&key (x 1 xp))
(if xp
`(foo :x ,x)
'(foo)))
处理此问题的一种方法是简单地禁止lambda列表关键字:
(defmacro define-remote (name (&rest args) &body decls/forms)
(dolist (a args)
(when (member a lambda-list-keywords)
(error "can't hack lambda list keywords in ~A, sorry" args)))
(let ((remote-name (intern (format nil "REMOTE-~A" (symbol-name name))))
(remote-defun-name (intern (format nil "REMOTE-DEFUN-~A"
(symbol-name name))))
(def `(defun ,name (,@args) ,@decls/forms)))
`(progn
,def
(defun ,remote-name (,@args) `(,',name ,,@args))
(defun ,remote-defun-name ()
',def)
',name)))
此版本的宏是有限的,但是正确的。