14

我想了解 Lisp 的内部结构,所以我想看看一切是如何实现的。

例如,

(macroexpand '(loop for i upto 10 collect i))

给我(在 SBCL 中)

(BLOCK NIL
  (LET ((I 0))
    (DECLARE (TYPE (AND NUMBER REAL) I))
    (SB-LOOP::WITH-LOOP-LIST-COLLECTION-HEAD (#:LOOP-LIST-HEAD-1026
                                              #:LOOP-LIST-TAIL-1027)
      (SB-LOOP::LOOP-BODY NIL
                          (NIL NIL (WHEN (> I '10) (GO SB-LOOP::END-LOOP)) NIL)
                          ((SB-LOOP::LOOP-COLLECT-RPLACD
                            (#:LOOP-LIST-HEAD-1026 #:LOOP-LIST-TAIL-1027)
                            (LIST I)))
                          (NIL (SB-LOOP::LOOP-REALLY-DESETQ I (1+ I))
                           (WHEN (> I '10) (GO SB-LOOP::END-LOOP)) NIL)
                          ((RETURN-FROM NIL
                             (SB-LOOP::LOOP-COLLECT-ANSWER
                              #:LOOP-LIST-HEAD-1026)))))))

但LOOP-BODY、WITH-LOOP-LIST-COLLECTION-HEAD等仍然是宏。如何完全扩展宏表单?

4

4 回答 4

16

要查看完整的扩展,需要在所有级别上遍历 Lisp 表单并扩展它们。为此,这个所谓的code walker必须理解 Lisp 语法(而不仅仅是 s-expression 语法)。例如在 中(lambda (a b) (setf a b)),列表(a b)是参数列表,不应进行宏扩展。

各种 Common Lisp 实现都提供了这样的工具。MACROEXPAND-ALLSBCL 提供的 6502 提到的答案。

如果使用开发环境,通常以命令的形式提供:

  • 史莱姆:M-x slime-macroexpand-allC-c M-m

  • LispWorks:菜单Expression > WalkM-x Walk Form,更短的M-Sh-m

于 2013-05-16T06:22:17.727 回答
11

其他答案非常适合您的问题,但您说您想看看一切是如何实现的。

许多宏(如您所知)是使用宏实现的,虽然macroexpand-all非常有用,但您可能会丢失宏负责什么更改的上下文。

一个不错的中间立场(如果您使用的是粘液)是使用slime-expand-1(Cc Enter),这表明扩展是另一个缓冲区。然后,您可以slime-expand-1在这个新缓冲区内使用来就地扩展宏。这使您可以在阅读时遍历扩展树,还可以使用撤消再次关闭扩展。

对我来说,这是理解其他人的宏的天赐之物。希望这对你也有帮助,玩得开心!

于 2013-05-16T12:40:09.480 回答
3

你可以尝试使用MACROEXPAND-ALL,但你可能得到的不一定有用。

真正LOOP的肉是宏本身,而不是生成的代码。

于 2013-05-16T05:56:29.007 回答
0

(注意:如果您对可移植性不感兴趣,SBCL 提供了macroexpand-all,它将满足您的需求。如果您想要可移植的解决方案,请继续阅读……)

快速而肮脏的解决方案是macroexpand表单本身,然后递归macroexpand除结果列表的第一个元素之外的所有元素。这是一个不完美的解决方案;当它尝试处理 alet的绑定时,它将完全失败(绑定列表的第一个参数let并不意味着宏扩展,但这段代码无论如何都会这样做)。

;;; Quick-and-dirty macroexpand-all
(defun macroexpand* (form)
  (let ((form (macroexpand form)))
    (cons (car form) (mapcar #'macroexpand (cdr form)))))

更完整的解决方案将特别考虑特殊形式,而不是宏观扩展其未评估的参数。如果需要,我可以使用这样的解决方案进行更新。

于 2013-05-16T06:07:35.700 回答