我想学习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等仍然是宏。如何完全展开宏窗体?
答案 0 :(得分:15)
要查看完整扩展,需要在所有级别上遍历Lisp表单并展开它们。为此,这个所谓的代码漫游器必须理解Lisp语法(而不仅仅是s表达式语法)。例如,在(lambda (a b) (setf a b))
中,列表(a b)
是参数列表,不应进行宏扩展。
各种Common Lisp实现提供了这样的工具。 6502的回答提到了MACROEXPAND-ALL
由SBCL提供。
如果您使用开发环境,它通常作为命令提供:
SLIME: M-x slime-macroexpand-all C-c M-m
LispWorks:menu Expression > 步行或 M-x Walk Form ,更短 M-Sh-m 。
答案 1 :(得分:10)
其他答案对你来说非常好,但你说你想看看一切是如何实现的。
许多宏(如您所知)已经使用宏实现,而 macroexpand-all 非常有用,但是您可能会丢失宏负责什么更改的上下文。
一个不错的中间地带(如果你使用粘液)是使用 slime-expand-1 (C-c Enter),它显示扩展是另一个缓冲区。然后,您可以在此新缓冲区中使用 slime-expand-1 来就地扩展宏。 这允许您在阅读时遍历树扩展,并使用undo再次关闭扩展。
对我而言,这是了解其他人的宏的神灵。希望这对你有所帮助,玩得开心!
答案 2 :(得分:3)
您可以尝试使用MACROEXPAND-ALL
,但您可能获得的并不一定有用。
在像LOOP
之类的东西中,真正的肉是宏本身,而不是生成的代码。
答案 3 :(得分:1)
(注意:如果你对便携性不感兴趣,SBCL会提供macroexpand-all
,这将完成您所追求的目标。如果您正在使用便携式解决方案,请继续阅读......)
快速和肮脏的解决方案是macroexpand
表单本身,然后递归macroexpand
除了结果列表的第一个元素。这是一个不完美的解决方案;它会在尝试处理let
的绑定时完全失败(let
的第一个参数,绑定列表,并不是宏扩展,但是这个代码无论如何都会这样做)
;;; Quick-and-dirty macroexpand-all
(defun macroexpand* (form)
(let ((form (macroexpand form)))
(cons (car form) (mapcar #'macroexpand (cdr form)))))
更完整的解决方案会特别考虑特殊形式,而不是宏观扩展其未评估的论点。如果需要,我可以使用这样的解决方案进行更新。