2012-01-04 14 views
1

我們來定義一個函數,它的主體包含宏,它將在某個未指定的時間展開,並在此過程中使用全局動態值*test*如何建立一個在宏展開時間內處於活動狀態的變量綁定?

> (defvar *test* nil) 
> (defmacro body() 
    `(print ,*test*)) 
> (defun test() 
    (body)) 
> (test) 
NIL 

但是,如果我想在函數定義綁定*test*,比方說,1,使之與生效,調用test此綁定操作的宏擴展生產1而不是NIL什麼。

只是包裹在defunlet不起作用:

> (let ((*test* 1)) 
    (defun test() 
     (body))) 
> (test) 
NIL 

也許,這是關係到這一行Hyperspec:

defun定義不需要執行任何編譯時的副作用

但是還有其他方法可以做到嗎?

+0

宏內部綁定有什麼問題? – sbenitezb 2012-01-04 15:15:47

+0

我已經編制了這個微不足道的例子來解決這個問題。但是,我的用例更復雜一些部分單獨生成,因此,基本上,我不能在宏內部執行此操作。 – 2012-01-04 15:18:32

+1

只需在宏的* test *之前刪除,以便在運行時評估* test *的值,而不是編譯時。雖然這可能會破壞你想要做的事情的目的? – 2012-01-05 16:33:38

回答

2

正如你自己寫的,宏在未指定的時間被擴展。在我的SBCL中,宏在整個表單被評估之前展開,這意味着在LET綁定生效之前。對於某些解釋器,當綁定到期後,該函數在執行時可能會擴展宏。

成爲Common Lisp的早期版本通過COMPILER-LET包含了這種機制,但它已被刪除。詳情請見COMPILER-LET-CONFUSION issue。從詞彙的角度看,可以使用MACROLET/SYMBOL-MACROLET來實現一些效果。動態地使這項工作理智是困難的,如果使用實際的動態綁定似乎是必要的,我會建議重新考慮這種方法。

0

我認爲這是因爲*test*變量在let的主體內有效。

0

你可以引入一個let這樣的:

(defvar *test* nil) 

(defmacro foo() 
    (let ((test (gensym))) 
    `(let ((,test *test*)) 
     (print ,test)))) 

(defun test-foo() 
    (foo)) 

(test-foo) => print and returns NIL 
(let ((*test* 1)) 
    (test-foo)) => print and returns 1 
0

怎麼樣使用宏讓利,而不是 (這裏是指定一個已知值到一個已知的變量控制評價的時間,但它 可以很容易地進行擴展,因爲我們正在玩 與動態變量來處理更多的變量):

(defmacro letter (&body body) 
    (let ((old-test *test*)) 
    (set '*test* 1) 
    `(progn 
     ,@body 
     (set '*test* ,old-test)))) 

定義test

(letter (defun test() (body))) 

使用test

CL-USER> (test) 

1 
1 

這似乎是工作在SBCL不如預期,需要 之前得到一些睡眠嘗試它的其他實現。

嗯,宏擴展很明顯,letter正常工作 只有宏擴展和EVALED。簡單的宏擴展不會 恢復*test*舊值(doh)。所以這不是一個好的'綁定 模擬器'。

相關問題