2012-10-19 57 views
2

我有一個循環內使用的列表,並且在每次迭代中,我都會應用一個將永久更改列表的函數(彈出和添加元素)。問題是,原始列表永遠不會更改,只要它是零。我該如何解決這個問題?我的代碼如下所示永久變更列表lisp

(defun looping-func() 
    (let ((queue '(2))) 
      (loop while (not (null queue)) do 
      (let ( (num (pop queue))) 
       (if (oddp num) 
        (format t "~%~A success" num) 
        (progn (format t "~%fail") 
          (add-to-list (1+ num) queue))))))) 

(defun add-to-list (elem l) 
    (nconc l (list elem))) 

如果列表包含多個元素,代碼將按預期工作。如果它恰好包含1個元素,那麼一旦該元素被彈出並且列表變爲零,則應用的更改不再是列表中的永久元素。我想這是因爲nconc是如何定義的,如果第一個參數是零,只是返回第二個沒有任何改變。有關如何去做這件事的任何想法?

PS:我知道上面的代碼是沒用的,但我對學校項目使用了相同的概念,所以我不幸發佈了代碼。

+3

你不應該在文字列表上使用'nconc'這樣的破壞性修改函數 - 修改文字會導致未定義的行爲。將'queue'的初始化更改爲'(list 2)'。 – Barmar

回答

3

變化

(add-to-list (1+ num) queue) 

(setq queue (add-to-list (1+ num) queue)) 

你不能nconc

(nconc nil . lists) 

「擴展」 nil相當於

(nconc . lists) 

所以,你需要把的add-to-list結果在queue

+0

那個把戲好人:)。我一直在遠離setf和setq函數,因爲我認爲他們會創建全局變量,我不想這樣做。我一定是錯了,我會再讀一遍。再次感謝。 – turingcomplete

+0

看起來像setf會做同樣的事情,它更推薦使用。 – turingcomplete

+2

以前未定義的變量的'setf'和'setq'都可能導致全局可見的動態變量出現。變量已經「存在」時不成問題。 – Vatine

1

因爲我認爲這是一個鍛鍊,這裏有一個例子,你不應該在日常實踐中使用,你應該使用push宏,這可能有類似的功能吧:

(defmacro push-example (item list) 
    (let ((the-list list))    ; we do this to prevent 
             ; multiple evaluations 
             ; of the `list' argument 
    `(setq ,the-list (cons ,item ,the-list)))) 

(defparameter *test* nil) 

(push-example 'foo *test*) ;; (foo) 
*test* ;; (foo) 

雖然你沒有問一個宏(你要的功能),道格的答案是技術上更正確的,這說明了如何可能使用代碼生成做到了通過宏觀。注意,這基本上和你的函數做的是一樣的,除了它可以封裝你不得不作的其他的調用setq

3

不要將元素添加到列表的末尾。

從來沒有。

Lisp中的列表是以向頭部添加元素便宜的方式設計的。增加到最後是潛在的昂貴。

要實現LIFO隊列,您需要一個不同的實現。

在運行時不要更改源代碼中的常量字面量數據。

正確縮進代碼。

+0

@turingcomplete:a)添加到前面?然後扭轉名單?不,這同樣糟糕。這同樣緩慢。 b)在Common Lisp中未定義後果c)只需使用編輯器縮進Lisp代碼即可。 –

+0

非常感謝您的幫助。我想最好用一個指向列表結尾的指針來實現列表結構? – turingcomplete