lisp宏可构建表达式列表及其求值

时间:2019-11-14 00:32:05

标签: macros common-lisp lisp-macros

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

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

我跑

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

我想它返回:

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

3 个答案:

答案 0 :(得分:4)

严格来说,宏本身无法做到;宏必须做的是生成代码,在其中嵌入参数表达式,以便对它们进行求值,并同时对它们进行引用。

鉴于(list-builder (+ x y) (+ y x) x),我们想生成以下代码:(list '(+ x y) (+ x y) '(+ y x) (+ y x) 'x x)

我们可以将宏拆分为用defmacro定义的顶级包装器和一个扩展器函数,该函数完成产生list参数的大部分工作;宏的正文仅在其上粘贴list符号并返回它。

宏辅助函数必须在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)))

在单个反引号表达式中全部位于一个defmacro中的“精巧”实现可能会像这样:

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

我们之前实施的“不支持点缀语法”检查现在从mapcan中变为错误。

lambda将每个表达式E变成列表((quote E) E)mapcan将这些列表链接在一起以形成list的参数,然后将其与(list ...)拼接成,@形式。

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

答案 1 :(得分:1)

当然,一个lisp宏可以做到这一点。由于lisp宏可以完全控制对其参数的评估。

仅在要使用递归的情况下才必须使用宏帮助器功能。由于宏存在递归调用自身的问题。

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

“更简单”的解决方案

(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构造中,将collect / collectingnconc结合使用,而不是nconcing / (list ...)控制元素如何组合在一起。

(list `',x x)

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

`',x

x的内容放入表达式中,而其引号则防止对为x放置的表达式进行评估。

外部listloop构造的拼接结合在一起, 最终捕获(阻止)对宏体的内在最终评估。

答案 2 :(得分:0)

(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> 
相关问题