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>
我正在尝试在 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>