宏以及如何跟踪它们
Macros and how to trace them
trace
宏对于调试非常有用。但在任何宏上使用时它都会停止。就像我尝试执行以下操作一样:
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 中是否有任何其他(非常规的)方法来跟踪宏?
Common Lisp 标准只提到函数的跟踪。在编译实现中,宏扩展通常发生在编译时,因此通常不支持跟踪宏。
但是某些 Common Lisp 实现可以通过 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 是另一个支持宏跟踪的实现。
So, what is the reason for not having any facility for tracing macros in Common Lisp?
如前所述,这是语言标准。除了语言标准实现之外,还以各种方式提供各种语言扩展,包括某些 Lisp 解释器 (!) 跟踪宏的能力。
如果代码已经编译,跟踪将无法正常工作。拥有 Lisp 解释器会有所帮助,但实现并不需要有解释器。这里的 Lisp 解释器是指一个执行引擎,它以 Lisp 代码作为数据工作。
在宏上使用 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
如果您的表单 returns 是一个使用宏的新表单,您可能想尝试 macroexpand
。这就像一遍又一遍地调用 macroexpand-1
,直到没有剩余的转换。
*macroexpand-hook*
是跟踪宏的预期方法。
http://www.lispworks.com/documentation/HyperSpec/Body/v_mexp_h.htm#STmacroexpand-hookST
trace
宏对于调试非常有用。但在任何宏上使用时它都会停止。就像我尝试执行以下操作一样:
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 中是否有任何其他(非常规的)方法来跟踪宏?
Common Lisp 标准只提到函数的跟踪。在编译实现中,宏扩展通常发生在编译时,因此通常不支持跟踪宏。
但是某些 Common Lisp 实现可以通过 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 是另一个支持宏跟踪的实现。
So, what is the reason for not having any facility for tracing macros in Common Lisp?
如前所述,这是语言标准。除了语言标准实现之外,还以各种方式提供各种语言扩展,包括某些 Lisp 解释器 (!) 跟踪宏的能力。
如果代码已经编译,跟踪将无法正常工作。拥有 Lisp 解释器会有所帮助,但实现并不需要有解释器。这里的 Lisp 解释器是指一个执行引擎,它以 Lisp 代码作为数据工作。
在宏上使用 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
如果您的表单 returns 是一个使用宏的新表单,您可能想尝试 macroexpand
。这就像一遍又一遍地调用 macroexpand-1
,直到没有剩余的转换。
*macroexpand-hook*
是跟踪宏的预期方法。
http://www.lispworks.com/documentation/HyperSpec/Body/v_mexp_h.htm#STmacroexpand-hookST