Macros 在公共Lisp中定义一组函数和函数调用的宏
起初,我希望它很简单——因为它是所有语言中最强大的宏工具的通用Lisp。但现在,8小时后,我的背面出现了逗号和准引号,我离解决方案已经不远了 我需要一个产生3个函数的宏。其签名如下:Macros 在公共Lisp中定义一组函数和函数调用的宏,macros,common-lisp,Macros,Common Lisp,起初,我希望它很简单——因为它是所有语言中最强大的宏工具的通用Lisp。但现在,8小时后,我的背面出现了逗号和准引号,我离解决方案已经不远了 我需要一个产生3个函数的宏。其签名如下: (defmacro remote (name arg-names &body body) ...) 它应该生成(我在下面的文本中使用“foo”作为名称参数): (defun foo(,arg name),@body)(只是foo的直接实现),可以作为(foo arg1 arg2…)调用 (defun r
(defmacro remote (name arg-names &body body) ...)
它应该生成(我在下面的文本中使用“foo”作为名称参数):
(defun foo(,arg name),@body)
(只是foo的直接实现),可以作为(foo arg1 arg2…)调用
(defun remote foo(args).
它可以作为(remote foo arg1 arg2…
调用并返回引用的调用。(foo arg1 arg2…
(defun remote-defun-foo()…
,当像这样调用时,(remote-defun-foo)
返回函数的引用定义(defun-foo(,args),@body)
(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)))
然后
fiddly位(这个答案的早期版本因为我误读了这个问题而出错)是第二种形式,您希望使用您创建的函数的参数创建一个带引号的调用,因此您必须稍微摆弄一下嵌套的反引号
我发现有一种方法有助于理解这一点,那就是用list
重写第二种形式,然后记住这一点
(list x y)
与
`(,x ,y)
(事实证明,我不知道如何在markdown中将反引号转换成内联代码。)
然而,所有这些都有一个令人讨厌的潜伏问题:扩展中的第二种形式通常是错误的。
> (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) ...))
现在远程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)))
此版本的宏有限但正确。如前所述,这并不难:
(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)))
然后
fiddly位(这个答案的早期版本因为我误读了这个问题而出错)是第二种形式,您希望使用您创建的函数的参数创建一个带引号的调用,因此您必须稍微摆弄一下嵌套的反引号
我发现有一种方法有助于理解这一点,那就是用list
重写第二种形式,然后记住这一点
(list x y)
与
`(,x ,y)
(事实证明,我不知道如何在markdown中将反引号转换成内联代码。)
然而,所有这些都有一个令人讨厌的潜伏问题:扩展中的第二种形式通常是错误的。
> (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) ...))
现在远程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)))
此版本的宏有限但正确