2013-10-12 55 views
2

我對Common Lisp破壞性的DELETE函數有點困惑。它似乎按預期方式工作,除了如果該項目是列表中的第一項:DELETE具有破壞性 - 但並非總是如此?

CL-USER> (defvar *test* (list 1 2 3)) 
*TEST* 
CL-USER> (delete 1 *test*) 
(2 3) 
CL-USER> *test* 
(1 2 3) 
CL-USER> (delete 2 *test*) 
(1 3) 
CL-USER> *test* 
(1 3) 

回答

5

「破壞性」並不意味着「到位工作」。這意味着所操作的價值結構可能會以某種任意且常常未定義的方式進行修改。在某些情況下,這可能會影響實施和案例,就好像它是「就地」一樣。然而,這通常不能被依賴。

如果使用了破壞性操作符,那麼在操作完成後,您要告訴編譯器您對變量的前一個值不感興趣。你應該假設這個價值在事後不再被認可,並且不再使用它。相反,你應該使用操作的返回值。

(let ((a (list 1 2 3))) 
    (let ((b (delete 2 a))) 
    (frob b)) 
    a) 

=> You were eaten by a grue. 

如果您不確定破壞性操作的安全性,使用他們的非破壞性的同行(remove在這種情況下)。

(let ((a (list 1 2 3))) 
    (let ((b (remove 2 a))) 
    (frob b)) 
    a) 

=> (1 2 3) 

如果你真的想修改變量的內容,將它們設置爲操作的返回值:

(let ((a (list 1 2 3))) 
    (setf a (delete 2 a)) 
    a) 

=> (1 3) 
+2

好吧,不是「變量的前一個值」作爲「至少這個變量保持的值的結構」。畢竟,共享結構並不罕見。 – Vatine

+1

它不侷限於「變量的值」。你可以做,例如'(刪除1(休息某個列表))'。它只需要一個列表作爲參數;它不是一個宏,也不需要_place_。 –

+0

@mck請注意,第一種情況下的結果並不像「你被一隻狼吃掉」那樣難以預測。雖然列表中任何缺陷單元格的車輛和cdr都可以修改,但它們仍然是缺陷單元格。在第一種情況下,'a'在調用'(delete 2 a)'後總是一個cons cell,即使它的car和cdr之後不同。更重要的是,它仍然是它以前的同類電池。例如,請參閱[此代碼示例](http://pastebin.com/qVUfHeV8)。 –

2

DELETE作品通過改變清單的前利弊細胞的CDR指向一個過去的元素( s)被刪除。但是,如果您要刪除列表的第一個元素,則不會修改先前的cons單元格。

雖然這個實現實際上並沒有被語言標準所規定,但實際上每個實現都是這樣工作的。

由於在刪除列表的第一個元素時沒有先前的缺陷單元修改,它只是返回第二個缺陷單元。所以即使DELETE被允許修改列表,您仍然必須將結果分配給您的變量來處理這種情況。另外,應該強調的是破壞行爲只允許由標準,而不是要求。所以有一種遙遠的可能性,即某些實現可能無法破壞性地實現它,並且您也必須考慮到這一點。

即使DELETE通過修改CAR而不是CDR來工作,仍然存在無法破壞性地執行刪除列表中的所有元素的情況,例如,

(setq *test* (list 'a 'a)) 
(delete 'a *test*) 

這產生一個空的列表,即NIL。但*test*仍然包含原始列表頭的cons單元,並且DELETE無法更改該單元。所以,你必須做到:

(setq *test* (delete 'a *test*)) 

設置*test*NIL

+1

這可能是一個常見的實現,但沒有指定真實。 'Delete'可以收集所有未刪除的元素並返回一個新的列表(即完全無損),或者可以將_n_未刪除的元素複製到列表中的第一個_n_ cons單元格的'car's中,並將_n_單元格的_cdr_設置爲「nil」。具體實現不是由標準決定的。您的描述也不會涵蓋例如'(刪除1(列表1))'和'(刪除1(列表1 2))'的情況,其中不需要修改; 'delete'可以簡單地返回列表參數的'cdr'。 –

8

請記住,DELETE應該在列表上工作,而不是在變量上工作。 DELETE通過列表(一個指向第一個cons的指針)而不是變量。

由於delete無法訪問變量*test*,因此無法更改它。 '它'在這裏意味着它的綁定。 *test*將指向與之前相同的cons單元。唯一可以改變的是cons單元格的內容或第一個cons單元格指向的其他cons單元格的內容。

有一件事是肯定的,不管DELETE做什麼,*test*總是會指向同一個cons單元。

從那得到什麼?如果你想有*test*點刪除操作的結果,那麼你必須明確地這樣說:

(setq *test* (delete 1 *test*))