Macros 在公共Lisp中定义一组函数和函数调用的宏

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

起初,我希望它很简单——因为它是所有语言中最强大的宏工具的通用Lisp。但现在,8小时后,我的背面出现了逗号和准引号,我离解决方案已经不远了

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

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

  • 我找到了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)))
    
    然后

    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)))
    
    此版本的宏有限但正确