2011-12-02 79 views
2

Paul Grahams ANSI Common Lisp書中的練習之一是:定義一個宏,它接受變量列表和一個代碼體,並確保變量恢復爲其原始值在評估代碼體之後。保存宏中參數的符號名

這次練習中遇到的問題是如何保存輸入變量的符號名稱。下面我有一個開始,我只保存符號綁定的值。

(defmacro save-run (varlist &body body) 
    `(let ((valuelist (list ,@varlist))) 
    (format t "valuelist: ~A" valuelist))) 

(let ((a 5)(b 6)) 
    (values '(a b)) 
    (save-run (a b) 
     (setf a 7) 
     (setf b 8))) 

[507]> valuelist: (5 6) 

編輯:這是一個解決方案,其中變量保存,然後恢復(使用下面的finnw提示)。但是在Vatine的答案中隱藏變量可能更優雅。

(defmacro save-run (varlist &body body) 
    `(let ((valuelist (list ,@varlist))) 
    ,@body 
    (multiple-value-setq ,varlist (values-list valuelist)))) 
+0

您不需要'(progn,@ body)'。只是',@ body'會有同樣的效果。 – finnw

+0

如果你使用'(setf(values,@ varlist)...)'而不是'(multiple-value-setq,varlist ...)',那麼一些變量可以是符號以外的地方,例如, '(save-run((cdr x))...)' – finnw

+0

不確定你在這裏是什麼意思。我查看了文檔,並且setf只能成對分配偶數個參數,而不是列表。 – snowape

回答

3

要獲取的符號列表,所有你需要做的就是報價VARLIST內容:

(defmacro save-run (varlist &body body) 
    `(let ((namelist ',varlist) 
     (valuelist (list ,@varlist))) 
    (format t "namelist: ~A~%" namelist) 
    (format t "valuelist: ~A~%" valuelist))) 

我懷疑這會不會是在最後defnition有用但是。在運行時對符號列表做的事情不多。相反,尋找一個在宏擴展中插入列表的好地方。你

可能還需要使用GENSYM,而不是硬編碼的變量名VALUELIST

(defmacro save-run (varlist &body body) 
    (let ((valuelist (gensym))) 
    `(let ((,valuelist (list ,@varlist))) 
     ,@body 
     (setf (values ,@varlist) (values-list ,valuelist))))) 
+0

不錯,不知道你可以結合報價和逗號。 – snowape

+0

對不起,我應該說不要使用*名單*,你確實需要價值清單。我的錯。 – finnw

2

就個人而言,我會介紹我們想要保存的變量另一個結合層。

你的變量varlist列表等等這樣的事情可能工作:

(defmacro save-run (varlist &body body) 
    `(let ,(loop for var in varlist 
       collect (list var var)) 
    ,@body)) 
+0

我喜歡它,比保存和恢復更優雅。 – snowape

1

我真的很喜歡Vatine的做法。這裏是擴展到相同代碼的實現,但使用mapcar代替循環宏:

(defmacro save-run (varlist &body body) 
    `(let ,(mapcar #'list varlist varlist) 
    ,@body))