2017-02-18 77 views
0

這個問題是關於通用Lisp setf宏,以及它如何評估其參數形式(和子形式) - 即只有一次,如果它們碰巧不止一次出現。 (這也部分跟進在Using get-setf-expansion在評論中給出的例子。)setf形式的評估

;create a list of two hash tables 
* (defparameter hts (list (make-hash-table) (make-hash-table))) 
HTS 
* hts 
(#<HASH-TABLE :TEST EQL :COUNT 0 {1007F76CB3}> 
#<HASH-TABLE :TEST EQL :COUNT 0 {1007F77103}>) 

;define a function that swaps the position of the two hash tables 
* (defun next-ht (hts) 
    (rotatef (first hts) (second hts)) 
    (second hts)) 
NEXT-HT 

交換:

;now do a swap to verify it works 
* (next-ht hts) 
#<HASH-TABLE :TEST EQL :COUNT 0 {1007F76CB3}> 
* hts 
(#<HASH-TABLE :TEST EQL :COUNT 0 {1007F77103}> 
#<HASH-TABLE :TEST EQL :COUNT 0 {1007F76CB3}>) 

;and swap them back 
* (next-ht hts) 
#<HASH-TABLE :TEST EQL :COUNT 0 {1007F77103}> 
* hts 
(#<HASH-TABLE :TEST EQL :COUNT 0 {1007F76CB3}> 
#<HASH-TABLE :TEST EQL :COUNT 0 {1007F77103}>) 

進一步測試:

;then set different values for a key in each table 
* (setf (gethash 0 (first hts)) 11) 
11 
* (setf (gethash 0 (second hts)) 22) 
22 
* hts 
(#<HASH-TABLE :TEST EQL :COUNT 1 {1007F76CB3}> 
#<HASH-TABLE :TEST EQL :COUNT 1 {1007F77103}>) 

;finally execute a setf with a swapping side-effect 
* (setf (gethash 0 (next-ht hts)) (1+ (gethash 0 (next-ht hts)))) 
23 

;but it looks like hts has been swapped twice 
;back to its original state 
* hts 
(#<HASH-TABLE :TEST EQL :COUNT 1 {1007F76CB3}> 
#<HASH-TABLE :TEST EQL :COUNT 1 {1007F77103}>) 

;also, where did the initial value of 11 go? 
* (gethash 0 (first hts)) 
23 
T 
* (gethash 0 (second hts)) 
22 
T 
* 

可有人澄清所發生的事情?另外,帶有副作用的setf表達式的含義是什麼?

+1

'SETF'不會阻止從子表單被評估兩次。修改像'INCF'這樣的宏。 – jkiiski

+0

@jkiiski這是我正在尋找的區別。我以前的印象是'setf' *是某種修改宏,因爲這個地方只是傳遞給'get-setf-expansion'。謝謝。 – davypough

回答

4

爲什麼不用宏展開setf表? LispWorks:

CL-USER 32 > (pprint (macroexpand '(setf (gethash 0 (next-ht hts)) 
             (1+ (gethash 0 (next-ht hts)))))) 

(LET* ((#:|key1014| 0) 
     (#:|table1015| (NEXT-HT HTS)) 
     (#:|default1016| NIL) 
     (#:|store1017| (1+ (GETHASH 0 (NEXT-HT HTS))))) 
    (SYSTEM::%PUTHASH #:|key1014| #:|table1015| #:|store1017|)) 

它是做什麼的?

  • 拿到鑰匙值
  • 獲得哈希表,要求NEXT-HT
  • 獲得默認值,而不是使用
  • 獲得新的價值,呼籲NEXT-HT
  • 存儲新的key/value進入哈希表,使用一些具體的實現方式

那麼清楚NEXT-HT是叫兩次。

它背後的粗糙(!)概念模型是什麼?

  • setf將檢查第一個表達式。
  • 這是什麼?哦,這是gethash,讓我建立了二傳手的形式爲它
  • 的setter形式,然後從第一種形式評估所需的子窗體
  • 它就會計算出新的價值
  • 二傳手運營商將被用這些論據。

實施例:

CL-USER 62 > (setf (gethash (print 0) 
          (print (next-ht hts)) 
          (print 1)) 
        (print (1+ (print (gethash 0 
               (print (next-ht hts)) 
               2))))) 

0 
#<EQL Hash Table{1} 402000137B> 
1 
#<EQL Hash Table{0} 4020001573> 
2 
3 
3 ; return value 
+0

感謝您總結'setf'評估模型。但是我試圖更好地理解(參考上面的Code Review帖子),爲什麼acelent明確推薦使用setf表達式來編寫自己的帶有'get-setf-expansion'的修改宏。這是因爲你自己必須小心評估一次子表單; 'get-setf-expansion'不會爲你做這件事。這是正確的嗎?它可能略微偏離主題,但我希望看到類似get-setf-expansion操作的摘要。 – davypough

+0

我花了一段時間在上述代碼評審帖子中挖掘信息,現在我意識到我並不需要問這個問題。大多數答案已經在那裏。如果您有任何要添加的內容,請隨時將其刪除,或將其用於進一步的教育目的。但是感謝時間,精力和澄清。 – davypough