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)
严格来说,宏本身无法做到这一点;宏必须做的是生成代码,在这些代码中,参数表达式以它们被评估的方式嵌入,并且以它们被引用的方式嵌入。
鉴于(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).