2011-06-01 77 views
2

我正在嘗試在Lisp中編寫一個使用自身重新實現let的宏。這是一個沒有實際目的的平凡練習;然而,在給出一個response相關的問題後,我意識到我應該更多地瞭解宏。他們被吹捧爲關於Lisp的偉大事情之一,但我很少使用它們。Lisp宏的問題

無論如何,這是我第一次嘗試:

(defmacro mylet (args &rest exp) `(let ,args (dolist (x ,exp) x))) 

但是當我嘗試類似:

(mylet ((a 5) (b 2)) (print (+ a b))) 

此拋出了一個錯誤:

#1=(PRINT (+ A B)) is not a symbol or lambda expression in the form (#1#) . 

ARGS(一和b)設置正確,但打印語句不起作用。我認爲這是因爲我使用了兩個間接層次 - 指的是我在宏中創建的一個變量。但我似乎無法弄清楚如何解決它!有任何想法嗎?

回答

4

您的宏擴展爲:

(LET ((A 5) (B 2)) 
    (DOLIST (X ((PRINT (+ A B)))) X)) 

這是無效的,因爲((PRINT (+ A B)))不是有效的表達。還有一個問題是,在宏擴展中使用實名符號可能導致變量捕獲,但這不直接相關(請參閱PCL中的更多內容)。

在這裏使用DOLIST是不必要的,而且編譯得到正確的(您必須將所有子表單轉換爲匿名函數才能將它們粘貼到列表中,然後按順序對其進行調用,然後存儲最終結果以符合PROGN行爲)。你可以使用progn這個,或者,因爲LET包括隱含progn這個,只是拼接使用反引號機制,@功能體:

(defmacro mylet (args &body exp) `(let ,args ,(cons 'progn exp))) 

(defmacro mylet (args &body exp) `(let ,args ,@exp)) 
+0

謝謝,這是從來沒有想過使用cons + progn這個有很大的answer--我不知道拼接。但是,我沒有看到你在哪裏((print(+ a b)))...它不應該試圖評估(print(+ a b))嗎?我錯過了什麼?即使沒有dolist,像(first,exp)這樣簡單的東西也不起作用。 – Jeff 2011-06-01 18:37:54

+0

我從[宏展開器](http://www.lispworks.com/documentation/HyperSpec/Body/f_mexp_.htm)獲得了'((print(+ ab))',這是測試宏是否擴展爲你認爲他們做了什麼&rest /&body(這些是相同的,不同之處只對讀者有意義)參數始終是所有剩餘參數的列表,因此在宏展開時變量'exp'的值是打印表示'((print(+ ab))',它被按原樣插入到結果代碼中,然後代碼就像輸入代碼一樣執行。 – Ramarren 2011-06-01 19:09:21

+0

哎呀,應該是'那麼它會被當作一個列表來處理......但即使存在,它仍然不起作用 – Jeff 2011-06-01 19:32:39