6

我编写了我的愚蠢函数,它返回一个没有通用 lisp 中最后一个元素的列表。这个问题有没有更优雅的解决方案?

这是我的代码:

(defun list-without-last (l)
  (if (> (length (rest l)) 0)
      (append (list (first l)) (list-without-last (rest l)))
      nil))
4

6 回答 6

13

简短而简单,就像 Lisp 一样。这是神奇的东西:

(defun without-last(l) (reverse (cdr (reverse l))) )

于 2014-04-09T21:58:18.717 回答
9

您的函数有两个问题:

  • 您正在使用长度。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)                                                                                                                                             
于 2012-05-17T13:26:03.597 回答
2

有时您可能会发现自己需要修改列表而不是制作副本,在这种情况下,这可能会很方便:

(defun butlast! (x)
  (do ((y x (cdr y)))
      ((null (cddr y))
       (and (rplacd y nil) (return x)))))
于 2012-05-18T20:19:39.157 回答
0

正如上面提到的 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/)。

于 2013-11-06T00:06:46.730 回答
0

关于什么:

(defun butlast2 (L)
  (if (null (rest L))
    nil
    (cons (first L) (butlast2 (rest L)))
  )
)
于 2018-03-05T18:58:39.113 回答
0
(defun remove-last (lst)
  (do ((l lst (rest l))
       (res '()))
      ((null (rest l)) (nreverse res))
    (push (first l) res)))
于 2019-01-09T08:46:08.933 回答