2015-09-02 56 views
1

Lisp newbie here。Common Lisp:無法獲取Paul Graham的書中的解壓縮函數

我正在閱讀Paul Graham的書,ANSI Common Lisp。

在頁38是uncompress函數。它需要一個成對的列表,其中成對的第一個項目是一個數字,表示應該有多少第二個項目。例如,解壓縮這樣的:

((3 a) (2 b) c) 

應該產生這樣的:

(A A A B B C) 

我鍵入uncompress函數成Lisp解釋(GCL-2.6.2-ANSI),然後進行測試它像這樣:

(uncompress '((3 A) B (2 C) (5 D))) 

生成此錯誤消息:

Error in IF [or a callee]: Too many arguments. 

Fast links are on: do (use-fast-links nil) for debugging 
Broken at IF. Type :H for Help. 
1 (Abort) Return to top level. 

以下是uncompress函數。我認爲我忠實地輸入了書中的內容。我測試了每件作品,並且每件作品似乎都能正常工作。說實話,我卡住了。我不知道是什麼原因導致了錯誤。我會很感激你的幫助。

(defun uncompress (lst) 
    (if (null lst) 
     nil 
     (let (elt (car lst)) 
      (rest (uncompress (cdr lst)))) 
     (if (consp elt) 
      (append (apply #'list-of elt) 
        rest) 
      (cons elt rest)))) 

(defun list-of (n elt) 
    (if (zerop n) 
     nil 
     (cons elt (list-of (- n 1) elt)))) 

回答

3

您有最典型的常見lisp語法錯誤:不正確地使用括號!

這裏是正確的版本:

(defun uncompress (lst) 
    (if (null lst) 
     nil 
     (let ((elt (car lst)) 
      (rest (uncompress (cdr lst)))) 
     (if (consp elt) 
      (append (apply #'list-of elt) 
        rest) 
      (cons elt rest))))) 

由於這些樣的錯誤是如此普遍在沒有使用專門的編輯,我建議你使用Emacs等或者vim編輯器編輯您的程序。

+2

不,你的'let'中的變量綁定周圍的參數太少了。這不是Clojure。 :-) –

+1

OPS,有多諷刺!謝謝,糾正。 – Renzo

+1

啊!我看到了失蹤的左派。謝謝!它現在工作完美。順便說一句,我非常喜歡Lisp;這樣一種美麗的語言。 –

6

如果你使用編輯器縮進工具,代碼如下所示:

(defun uncompress (lst) 
    (if (null lst) 
     nil 
    (let (elt (car lst)) 
     (rest (uncompress (cdr lst)))) 
    (if (consp elt) 
     (append (apply #'list-of elt) 
       rest) 
     (cons elt rest)))) 

這將使它更容易發現這個錯誤。從句法上看,這是錯誤的,因爲IF不會超過三種形式。

(defun uncompress (lst) 
    (if (null lst) ; the IF has four subforms, one too many 
     nil 
    (let (elt (car lst)) ;<- variables ELT and CAR? Makes no sense 
     (rest (uncompress (cdr lst)))) ; <- not using the result? 
    (if (consp elt) ; <- fourth form in IF? Does not make sense. 
     (append (apply #'list-of elt) 
       rest) 
     (cons elt rest)))) 

Common Lisp中既IFLET特種作業帶有內置的語法。

在Lisp爲LET語法通常是:

let ({var | (var [init-form])}*) form* => result* 

Common Lisp中,可以在體內形成LET的頂部添加聲明:

let ({var | (var [init-form])}*) declaration* form* => result* 

的語法IF在Common Lisp中是:

if test-form then-form [else-form] => result* 

縮進

通常手動縮進Lisp代碼並不是一個好主意。讓編輯器或IDE做到這一點。確保所有代碼都正確縮進。

如果您有語法問題:首先重新縮進表達式 - >這確保代碼正確縮進,然後使查找問題更容易。接下來編譯代碼並讀取編譯器錯誤消息。 Common Lisp擁有出色的編譯器,並且有一些非常好的錯誤報告。

代碼

的代碼是不是很大呢:它使用遞歸,其中高階函數存在或LOOP會更好

這個版本有兩個:高階MAPCANLOOP

(defun uncompress (list) 
    (mapcan #'expand-item list)) 

(defun expand-item (item) 
    (typecase item 
    (atom (list item)) 
    (cons (destructuring-bind (n element) item 
       (loop repeat n collect element))))) 
+2

@RogerCostello另請參見['mappend'](https://common-lisp.net/project/alexandria/draft/alexandria.html#Conses)[亞歷山大](https:// common -lisp.net/project/alexandria/)。 – coredump

+2

@coredump:MAPCAN在這裏很好,因爲這個列表是新鮮的,可以很好地鏈接。 ;-) –

+1

是的,MAPCAN在這裏是完美的,我同意。 – coredump