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

2ab*_*ses 2 macros common-lisp lisp-macros

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

(defmacro list-builder (&rest exp)
    ...)
Run Code Online (Sandbox Code Playgroud)

我跑

(let ((X 1) (Y 2)) (list-builder (+ X Y) (- Y X) X))
Run Code Online (Sandbox Code Playgroud)

我希望它返回:

'((+ X Y) 3 (- Y X) 1 X 1)
Run Code Online (Sandbox Code Playgroud)

到目前为止我能做的最好的是使用代码获取表达式列表

(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)
Run Code Online (Sandbox Code Playgroud)

Kaz*_*Kaz 5

严格来说,宏本身无法做到这一点;宏必须做的是生成代码,在这些代码中,参数表达式以它们被评估的方式嵌入,并且以它们被引用的方式嵌入。

鉴于(list-builder (+ x y) (+ y x) x)我们想产生这样的代码:(list '(+ x y) (+ x y) '(+ y x) (+ y x) 'x x)

我们可以将宏拆分为一个由 定义的顶级包装器defmacro和一个扩展器函数,该函数执行生成list参数的大部分工作;宏的主体只是将list符号贴在它上面并返回它。

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

(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)))
Run Code Online (Sandbox Code Playgroud)

在一个defmacro反引号表达式中,一个“光滑”的实现可能是这样的:

(defmacro list-builder (&rest exprs)
  `(list ,@(mapcan (lambda (expr) (list `',expr expr)) exprs)))
Run Code Online (Sandbox Code Playgroud)

我们之前实施的“不支持点式语法”检查现在变成了mapcan.

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

该形式`',expr遵循将引用速记应用于`(quote ,expr).