2017-07-02 61 views
0

宏对于调试非常有用。但在任何宏观上使用时,它会停下来。就像如果我尝试做到以下几点:宏以及如何跟踪它们

CL-USER> (trace push) 

然后,它会给出一个错误说:

can't use encapsulation to trace anonymous function #<FUNCTION (MACRO-FUNCTION 
                   PUSH) {100053FB9B}> 
    [Condition of type SIMPLE-ERROR] 

嗯,这是显而易见的,因为trace的CLHS页,清楚地界定它在职能。那么,没有任何工具在Common Lisp中跟踪宏的原因是什么?
有没有其他(非传统)的方式来跟踪Common Lisp中的宏?

+1

那么,如果宏在编译时被扩展,你将如何跟踪它们的调用?您可以编写一个宏来选择扩展宏的第一种形式,并在其上调用“trace”。 – Carcigenicate

+1

在什么情况下你想跟踪一个宏?在SBCL中,使用非标准的':ENCAPSULATE'参数来追踪:'(trace push:encapsulate nil)'似乎是可能的。通常宏展开是调试宏的更好方法(使用Sly,你也可以使用宏步)。宏中任何复杂的逻辑都可以在一个单独的函数中实现,这个函数也可以被正常地跟踪。 – jkiiski

+0

谢谢@jkiiski!当我需要知道它在程序中实际做了什么时,宏扩展内置宏(比如'push')不会有太大帮助。 – Mooncrater

回答

4

Common Lisp标准只提到函数的跟踪。

但有些Common Lisp的实现可以跟踪宏:

CLISP可以跟踪宏

[1]> (defmacro foo (a) a) 
FOO 
[2]> (trace foo) 
;; Tracing macro FOO. 
(FOO) 
[3]> (loop for i below 4 collect (foo i)) 
1. Trace: (FOO I) 
1. Trace: FOO ==> I 
1. Trace: (FOO I) 
1. Trace: FOO ==> I 
1. Trace: (FOO I) 
1. Trace: FOO ==> I 
1. Trace: (FOO I) 
1. Trace: FOO ==> I 
(0 1 2 3) 

LispWorks是支持跟踪宏的另一种实现方式。

那么,没有任何工具在Common Lisp中跟踪宏的原因是什么?

如上所述,这是语言标准。除了语言之外,标准实现以各种方式提供各种语言扩展,包括一些Lisp解释器(!)跟踪宏的能力。

如果代码已经编译完成,跟踪将不起作用。有一个Lisp解释器有帮助,但实现不需要有解释器。这里的Lisp解释器意味着一个执行引擎,它使用Lisp代码作为数据。

2

在宏观使用trace似乎有点奇怪,但它在CLISP工作:

(trace push) 
(defparameter *stack* '()) 

(defun push-xy (x y) 
    (push x *stack*) 
    (push y *stack*)) 
; 1. Trace: (push x *stack*) 
; 1. Trace: push ==> (setq *stack* (cons x *stack*)) 
; 1. Trace: (push y *stack*) 
; 1. Trace: push ==> (setq *stack* (cons y *stack*)) 
; ==> push-xy 

该标准不说什么时候应该扩大宏,以便定义函数和lambda表达式时,这可能会发生,编译并且有时被调用。一些实现运行宏两次,所以你得到两倍的输出。

我从来没有使用过这个。我宁愿用macroexpand-1

(macroexpand-1 '(push x *stack))) 
; ==> (setq *stack (cons x *stack)) 
; ==> t 

如果您的形式返回使用宏的新形式,你可能想尝试macroexpand来代替。它像一次又一次地呼叫macroexpand-1,直到没有转换。

相关问题