宏以及如何跟踪它们

时间:2017-07-02 15:11:28

标签: debugging macros common-lisp trace

trace宏对于调试非常有用。但它在任何宏观上使用时都会停止。就像我尝试执行以下操作一样:

 document.getElementById('<%= txt_field01.ClientID %>').value = '';
 document.getElementById('<%= txt_field01.ClientID %>').value = '';

然后,它会说错误:

CL-USER> (trace push)

嗯,这很明显,因为trace的clhs页面明确地定义了函数。那么,在Common Lisp中没有任何跟踪宏的工具的原因是什么?
是否还有其他(非常规)方法来跟踪Common Lisp中的宏?

3 个答案:

答案 0 :(得分: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代码作为数据工作。

答案 1 :(得分: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,直到没有转变为止。

答案 2 :(得分:0)