我编写了我的傻功能,它返回一个没有普通lisp中最后一个元素的列表。这个问题还有更优雅的解决方案吗?
这是我的代码:
(defun list-without-last (l)
(if (> (length (rest l)) 0)
(append (list (first l)) (list-without-last (rest l)))
nil))
答案 0 :(得分:10)
简短而简单,就像Lisp一样。 这是神奇的东西:
(defun without-last(l)
(reverse (cdr (reverse l)))
)
答案 1 :(得分:8)
您的功能有两个问题:
你正在使用LENGTH。 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)
答案 2 :(得分:1)
有时您可能会发现自己需要修改列表而不是复制,在这种情况下这可能很方便:
(defun butlast! (x)
(do ((y x (cdr y)))
((null (cddr y))
(and (rplacd y nil) (return x)))))
答案 3 :(得分:0)
正如上面提到的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/)。
答案 4 :(得分:0)
怎么样:
(defun butlast2 (L)
(if (null (rest L))
nil
(cons (first L) (butlast2 (rest L)))
)
)
答案 5 :(得分:0)
(defun remove-last (lst)
(do ((l lst (rest l))
(res '()))
((null (rest l)) (nreverse res))
(push (first l) res)))