我编写了我的愚蠢函数,它返回一个没有通用 lisp 中最后一个元素的列表。这个问题有没有更优雅的解决方案?
这是我的代码:
(defun list-without-last (l)
(if (> (length (rest l)) 0)
(append (list (first l)) (list-without-last (rest l)))
nil))
我编写了我的愚蠢函数,它返回一个没有通用 lisp 中最后一个元素的列表。这个问题有没有更优雅的解决方案?
这是我的代码:
(defun list-without-last (l)
(if (> (length (rest l)) 0)
(append (list (first l)) (list-without-last (rest l)))
nil))
简短而简单,就像 Lisp 一样。这是神奇的东西:
(defun without-last(l)
(reverse (cdr (reverse l)))
)
您的函数有两个问题:
您正在使用长度。LENGTH 必须扫描整个列表。
您正在使用附加。尝试使用缺点。缺点更简单。
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,您应该使用 common 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/)。
关于什么:
(defun butlast2 (L)
(if (null (rest L))
nil
(cons (first L) (butlast2 (rest L)))
)
)
(defun remove-last (lst)
(do ((l lst (rest l))
(res '()))
((null (rest l)) (nreverse res))
(push (first l) res)))