定义一个宏,该宏在Common Lisp中定义一组函数和函数调用



一开始,我认为它很简单,因为它是Common Lisp,拥有所有语言中最强大的宏功能。但现在,8个小时后,我的背面出现了逗号和准引号,我离解决方案不远了。

我需要一个能产生3个函数的宏。其签名如下:

(defmacro remote (name arg-names &body body) ...)

它应该生成(我在下面的文本中使用"foo"作为名称参数(:

  1. (defun foo (,arg-names) ,@body )(只是foo的直接实现(,可以作为(foo arg1 arg2 ... )调用。

  2. CCD_ 3,其可以被调用为CCD_。(foo arg1 arg2 ...)

  3. (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)))

此版本的宏是有限的,但是正确的。

最新更新