如何评估宏中 lambda 中的表单?

How do I evaluate form inside lambda that is within a macro?

我在使用以下宏时遇到问题:

(defmacro gather-params (&rest body)
  "Return plist of params"
  `(concatenate 'list
        (map 'list
             #'(lambda (plist)
                 (if (typep (first plist) 'keyword)
                     (cons 'list plist)
                     plist))
             ',body)))

在宏中,我无法通过在其前面添加逗号来使 plist 求值,例如:,plist 当我这样做时,编译器会抱怨变量 ,plist 不存在。

关于宏中的范围,我有什么不明白的地方吗?

当前结果:

input:  (gather-params (:mykey (+ 1 1)) (list 1 2 3))
result:   ((LIST :MYKEY (+ 1 1)) (LIST 1 2 3))

想要的结果:

input:  (gather-params (:mykey (+ 1 1)) (list 1 2 3))
result:   ((LIST :MYKEY 2) (LIST 1 2 3))

(concatenate 'list '(1 2 3)) 就是 (copy-list '(1 2 3))

-> 两者的计算结果都是 (1 2 3).

宏生成代码。它有一个反引号列表。这个反引号列表是在宏扩展时计算的 -> 当宏扩展发生时,例如在编译期间。

生成的代码有一个带有参数的函数,名为 plist。该函数在运行时执行。它在宏展开期间不存在。因此 plist 在宏展开期间没有变量。因此,在宏展开期间,您无法计算 plist 的值,因为那时该变量不存在。

如果你想评估代码,那么 Common Lisp 有函数 eval。例如,可以在运行时调用 eval

CL-USER 31 > (defmacro gather-params (&rest body)
              "Return plist of params"
              `(map 'list
                    #'(lambda (plist)
                        (if (typep (first plist) 'keyword)
                            (list 'list
                                  (first plist)
                                  (eval (second plist)))
                            plist))
                    ',body))
GATHER-PARAMS

CL-USER 32 > (gather-params (:mykey (+ 1 1)) (list 1 2 3))
((LIST :MYKEY 2) (LIST 1 2 3))

查看此示例,使用不同的方法:

CL-USER 42 > (defmacro gather-params (&rest body)
               "Return plist of params"
               `(list ,@(mapcar
                         (lambda (plist)
                           (if (typep (first plist) 'keyword)
                               (list 'list
                                     ''list
                                     (first plist)
                                     (second plist))
                             plist))
                         body)))
GATHER-PARAMS

CL-USER 43 > (let ((foo 1))
               (gather-params (:mykey (+ foo foo)) (list 1 2 3)))
((LIST :MYKEY 2) (1 2 3))