common-lisp - 如何评估宏内的 lambda 内的表单?

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

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

回答1

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

-> 两者都评估为 (1 2 3)

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

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

如果你想评估代码,那么 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))

相似文章