我写了我的愚蠢函数,它返回一个没有最后一个元素的普通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:追加不会复制最后一个列表。 –