2017-07-02 106 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,直到沒有轉換。

相關問題