我寫了我的愚蠢函數,它返回一個沒有最後一個元素的普通lisp列表。有沒有更好的解決方案來解決這個問題?返回列表中沒有最後一個元素的公共lisp
這裏是我的代碼:
(defun list-without-last (l)
(if (> (length (rest l)) 0)
(append (list (first l)) (list-without-last (rest l)))
nil))
我寫了我的愚蠢函數,它返回一個沒有最後一個元素的普通lisp列表。有沒有更好的解決方案來解決這個問題?返回列表中沒有最後一個元素的公共lisp
這裏是我的代碼:
(defun list-without-last (l)
(if (> (length (rest l)) 0)
(append (list (first l)) (list-without-last (rest l)))
nil))
你的功能有兩個問題:
。 LENGTH必須掃描整個列表。
您正在使用APPEND。嘗試使用CONS。 CONS更簡單。
Common Lisp也已經提供了這個功能。它被稱爲BUTLAST。
在實際的代碼中,我們也不會使用遞歸。堆棧大小會限制我們可以處理的列表的長度。
使用LOOP
宏觀的迭代版本:
CL-USER> (defun my-butlast (list)
(loop for l on list
while (rest l)
collect (first l)))
MY-BUTLAST
CL-USER> (compile 'my-butlast)
MY-BUTLAST
NIL
NIL
CL-USER> (my-butlast '(1 2 3 4 5))
(1 2 3 4)
CL-USER> (my-butlast '(1))
NIL
CL-USER> (my-butlast '(1 2))
(1)
有時你可能會發現自己需要修改的地方列表,而不是製作副本,在這種情況下,這可能是有用:
(defun butlast! (x)
(do ((y x (cdr y)))
((null (cddr y))
(and (rplacd y nil) (return x)))))
正如Rainer Joswig在上面提到的,您應該使用通用的lisp內置函數butlast
。
但是,如果你仍然希望看到一個有效的遞歸版本會是什麼樣子:
(defun butlast2 (list)
(labels ((butlast2-worker (list result)
(if (null list)
(nreverse result)
(let ((element (first list))
(rest (rest list)))
(if (null rest)
(nreverse result)
(butlast2-worker rest (cons element result)))))))
(butlast2-worker list())))
只要你的Lisp實現支持尾調用優化,這會被轉化成一個圈。訣竅是,無論何時調用butlast2-worker
,其結果都將直接返回,這意味着您不需要跟蹤以前函數調用的參數/內部變量。這最後意味着您不需要像通常爲遞歸函數那樣繼續填充調用堆棧。
看着Rainer Joswig的定義,你可以看到巨大的差異。看看loop
的力量,並學會如何使用它(或者更好:使用iterate
http://common-lisp.net/project/iterate/)。
簡單,就像Lisp一樣。 這裏是神奇的東西:
(defun without-last(l) (reverse (cdr (reverse l))) )
什麼:
(defun butlast2 (L)
(if (null (rest L))
nil
(cons (first L) (butlast2 (rest L)))
)
)
非常感謝butlast,順便說一句,有什麼用追加的問題? – Sergey
'APPEND'必須掃描整個列表,以及複製所有元素。如果您在一個循環中多次調用'APPEND',最終會得到一個函數,它會隨着添加更多元素而呈指數級下降。 –
@EliasMårtenson:追加不會複製最後一個列表。 –