2011-05-02 67 views
1

考慮這段代碼:Common Lisp的,參考值和實際值

(defvar lst '(1 1)) 

(defmacro get-x (x lst) 
    `(nth ,x ,lst)) 

(defun get-y (y lst) 
    (nth y lst)) 

現在讓我們假設,我想更改列表稱爲LST元素的值時, with get-x and the cdr with get-y。 當我試圖改變的價值與GET-X(與SETF)一切順利,但如果我得到-Y它標誌着一個錯誤(縮短)嘗試:

;抓到STYLE-WARNING: ;未定義函數:(SETF GET-STUFF)

爲什麼會發生這種情況?

我自己也懷疑,這是因爲宏只是展開和功能第n個簡單地返回到列表中的一個元素,而另一方面函數的值的參考評估函數調用第n並返回參考值的值(聽起來令人困惑)。

我糾正我的懷疑嗎? 如果我是正確的,那麼如何才能知道什麼僅僅是對一個值和一個實際值的引用?

回答

8

的誤差不與宏版本發生,因爲,當你認爲,表達(setf (get-x some-x some-list) some-value)將擴大(在編譯時)成類似(setf (nth some-x some-list) some-value)(不是真的,但the details of setf-expansion是複雜的),並且編譯器知道,如何處理該問題(即,存在爲功能nth定義的合適的setf擴展器)。

但是,在get-y的情況下,編譯器沒有setf擴展器,除非您提供一個擴展器。這樣做最簡單的方法是

(defun (setf get-y) (new-value x ls) ; Note the function's name: setf get-y 
    (setf (nth x ls) new-value)) 

注意,有關於setf -expanders幾個約定:

  1. 新的值總是提供作爲第一個參數setf功能
  2. 全部setf函數應該返回新的值作爲它們的結果(因爲這是,整個setf形式應該返回)

在Common Lisp(至少不是C++意義上的)中,沒有像「參考」這樣的概念,儘管曾經有位於位置的Lisp方言。廣義地點表單(即,setf及其機器)與純C++樣式引用的工作方式非常不同。如果您對細節感興趣,請參閱CLHS。

+0

我實際上並不認爲到目前爲止在C++意義上的Common Lisp中有引用,這要感謝清除它。我必須弄清楚我的問題的解釋,所以這就是我想出的。 用SETF GET-Y這樣的名稱來定義函數的這件事對我來說是一種全新的概念...無論如何,我會對此做更多的研究,感謝您的幫助! – Johan 2011-05-02 13:05:59

4

SETF是一個宏。

的想法是,設置和讀取數據結構元素是兩個操作,但通常需要兩個不同的名稱(或者甚至更復雜的東西)。 SETF現在使您只能使用兩個名稱:

(get-something x) 

上面顯示的是數據結構。反過來簡單地說就是:

(setf (get-something x) :foobar) 

上面用X設置數據結構:FOOBAR。

SETF不把(get-something x)當作參考或類似的東西。它只是每個操作都有一個反操作數據庫。如果你使用GET-SOMETHING,它知道反操作是什麼。

SETF如何知道它?簡單:你必須告訴它。

對於NTH操作,SETF知道如何設置第n個元素。這是內置於Common Lisp中的。

對於您自己的GET-Y操作,SETF不具有該信息。你必須告訴它。有關示例,請參閱Common Lisp HyperSpec。一個例子是使用DEFUN和(SETF GET-Y)作爲函數名。

還要注意以下問題的風格與你的例子:

  • LST不是一個DEFVAR變量一個好名字。使用* list *作爲名稱以明確它是由DEFVAR(或類似的)聲明的特殊變量。

  • '(1 2)是一個字面常量。如果你編寫一個Common Lisp程序,改變它的效果是不確定的。如果您想稍後更改列表,則應該將其與LIST或COPY-LIST之類的內容相提並論。

+0

+1忽略了以文字形式提供的數據的大小。 – Dirk 2011-05-02 12:31:55

+0

是的,我知道我確實應該在全局變量周圍使用星號等,我只是非常懶惰,想以儘可能最短的方式寫出我想要的東西,我的「真實」代碼看起來不像那樣。謝謝你的解釋! – Johan 2011-05-02 13:04:45