lisp 宏来构建表达式列表及其评估

lisp macro to build a list of an expression and it's evaluation

我正在尝试在 Common Lisp 中编写一个宏,它接受任意数量的表达式并构建一个包含每个表达式的列表,然后在一行中对其进行评估。例如,如果我将我的宏命名为

(defmacro list-builder (&rest exp)
    ...)

我运行

(let ((X 1) (Y 2)) (list-builder (+ X Y) (- Y X) X))

我想要 return:

'((+ X Y) 3 (- Y X) 1 X 1)

到目前为止我能做的最好的事情就是使用代码

获取表达式列表
(defmacro list-builder (&rest exp)
  `',@`(',exp ,exp))

INPUT: (let ((X 1) (Y 2)) (list-builder (+ X Y) (+ Y X) X))
'((+ X Y) (+ Y X) X)

严格来说,宏本身是做不到的;宏必须做的是生成代码,其中参数表达式以求值的方式嵌入,并以引用的方式嵌入。

给定 (list-builder (+ x y) (+ y x) x) 我们想生成此代码:(list '(+ x y) (+ x y) '(+ y x) (+ y x) 'x x).

我们可以将宏拆分为一个用 defmacro 定义的顶级包装器和一个扩展器函数,该函数负责生成 list 参数的大部分工作;宏的主体只是在其上粘贴 list 符号和 returns。

宏辅助函数必须用 Common Lisp 中的一点 eval-when 舞蹈来包装,以确保它们在可能处理宏的所有可能情况下都可用:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun list-builder-expander (exprs)
    (cond
      ((null exprs) nil)
      ((atom exprs) (error "list-builder: dotted syntax unsupported":))
      (t (list* `',(car exprs) (car exprs)
                (list-builder-expander (cdr exprs)))))))

(defmacro list-builder (&rest exprs)
  (cons 'list (list-builder-expander exprs)))

一个 "slick" 实现,全部在一个 defmacro 中,在单个反引号表达式中,可能如下所示:

(defmacro list-builder (&rest exprs)
  `(list ,@(mapcan (lambda (expr) (list `',expr expr)) exprs)))

我们之前实施的 "dotted syntax unsupported" 检查现在变成了 mapcan 的错误。

lambda 将每个表达式 E 转换为列表 ((quote E) E)mapcan 将这些列表连接在一起形成 list 的参数,然后将其拼接到 (list ...) 形式 ,@.

`',expr 形式是将引号 shorthand 应用于 `(quote ,expr)

当然,lisp 宏可以做到这一点。由于 lisp 宏提供了对其参数求值的完全控制。

只有在要使用递归的情况下才必须使用宏辅助函数。由于宏在递归调用自身时存在问题。

但是通过 loop 处理 &rest rest 参数,您可以生成可变参数宏(具有任意数量参数的宏)并且仍然控制每个参数的计算。 经过一些反复试验(宏构造是一个增量过程,因为宏是复杂的结构),我得到了

"simpler" 解决方案:

(defmacro list-builder (&rest rest)
  `(list ,@(loop for x in `,rest
                 nconcing (list `',x x))))

测试者:

(let ((X 1) 
      (Y 2)) 
  (list-builder (+ X Y) (- Y X) X))

;; ((+ X Y) 3 (- Y X) 1 X 1)

有时,在 loop 结构中,使用 nconc/nconcing(list ...) 结合使用 collect/collecting 来代替 collect/collecting可以更好地控制元素如何组合在一起。

(list `',x x)

确保第二个 x 得到评估,而第一个

`',x

x 的内容放入表达式中,而它的引用阻止了对 x.

放置的表达式的计算

外面的list结合loop构造拼接进去, 最终捕获(阻止)宏主体的内在最终评估。

(defmacro list-builder (&rest args)
   `(let ((lst ',args)
          (acc nil))
      (dolist (v lst)
         (push v acc)
         (push (eval v) acc))
      (nreverse acc)))

我们可以像您一样创建列表生成器宏来获取剩余参数(我只是将它们重命名为伪代码的 args)。我会在列表中创建一个表达式的引用列表 (lst),以及一个空列表 (acc) 来存储表达式以及它们稍后计算的任何内容。然后我们可以使用 dolist 遍历我们的列表并将每个表达式推入列表,然后是表达式上的 运行 eval 计算结果。然后我们最终可以使用 nreverse 来获得列表的正确顺序。

我们可以调用它:

(let ((x 1)
       (y 2))
   (declare (special x))
   (declare (special y))
   (list-builder (+ x y) (- y x) x))

结果将是:

((+ X Y) 3 (- Y X) 1 X 1)
CL-USER>