定义一个在 Common Lisp 中定义一组函数和函数调用的宏
Defining a macro which defines a set of functions and function calls in Common Lisp
起初,我希望它很简单 - 因为它是 Common Lisp,具有所有语言中最强大的宏功能。但是现在,8 小时后,我的背面出现了逗号和准引号,我离解决方案还差得很远。
我需要一个能产生 3 个函数的宏。它的签名如下所示:
(defmacro remote (name arg-names &body body) ...)
它应该生成(我在下面的文本中使用“foo”作为名称参数):
(defun foo (,arg-names) ,@body )
(只是 foo 的直接实现)可以调用为 (foo arg1 arg2 ... )
.
(defun remote-foo (args) ...
可以调用为 (remote-foo arg1 arg2 ...)
和 returns 引用调用。 (foo arg1 arg2 ...)
(defun remote-defun-foo () ...
像这样调用时 (remote-defun-foo)
returns 函数的引用定义 (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
第二种形式(这个答案的早期版本出错了,因为我误读了这个问题)是第二种形式,你想在其中使用你创建的函数的参数创建一个带引号的调用,所以你必须 fiddle 关于嵌套反引号。
我发现一种有助于理解的方法是根据 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-list 并将其转换为与之对应的 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-list 关键字:
(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)))
这个版本的宏是有限的,但是是正确的。
起初,我希望它很简单 - 因为它是 Common Lisp,具有所有语言中最强大的宏功能。但是现在,8 小时后,我的背面出现了逗号和准引号,我离解决方案还差得很远。
我需要一个能产生 3 个函数的宏。它的签名如下所示:
(defmacro remote (name arg-names &body body) ...)
它应该生成(我在下面的文本中使用“foo”作为名称参数):
(defun foo (,arg-names) ,@body )
(只是 foo 的直接实现)可以调用为(foo arg1 arg2 ... )
.(defun remote-foo (args) ...
可以调用为(remote-foo arg1 arg2 ...)
和 returns 引用调用。(foo arg1 arg2 ...)
(defun remote-defun-foo () ...
像这样调用时(remote-defun-foo)
returns 函数的引用定义(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
第二种形式(这个答案的早期版本出错了,因为我误读了这个问题)是第二种形式,你想在其中使用你创建的函数的参数创建一个带引号的调用,所以你必须 fiddle 关于嵌套反引号。
我发现一种有助于理解的方法是根据 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-list 并将其转换为与之对应的 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-list 关键字:
(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)))
这个版本的宏是有限的,但是是正确的。