【问题标题】:Return list without last element in common lisp返回普通lisp中没有最后一个元素的列表
【发布时间】:2012-05-17 13:18:46
【问题描述】:

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

这是我的代码:

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

【问题讨论】:

    标签: list common-lisp


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      你的函数有两个问题:

      • 您正在使用 LENGTH。 LENGTH 必须扫描整个列表。

      • 您正在使用 APPEND。尝试使用缺点。缺点更简单。

      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)                                                                                                                                             
      

      【讨论】:

      • 非常感谢butlast,顺便说一句,append有什么问题?
      • APPEND 必须扫描整个列表,并复制所有元素。如果你在一个循环中多次调用APPEND,你最终得到的函数会随着你添加更多元素而呈指数级增长。
      • @Elias Mårtenson:追加不会复制最后一个列表。
      • 可能需要修改递归注释以至少提及尾调用优化。只要您知道自己在做什么,递归就可以在“真实代码”中正常工作。
      • @ghollisjr 尾调用优化不是 Common Lisp 所要求的。编译器可以选择不这样做,如果启用了调试设置,通常会故意不这样做。不能如此轻易地追踪尾递归函数。用于迭代的 Scheme 风格递归不是 Common Lisp 的典型,因为它不能保证是迭代的,loop 通常工作得很好。
      【解决方案3】:

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

      (defun butlast! (x)
        (do ((y x (cdr y)))
            ((null (cddr y))
             (and (rplacd y nil) (return x)))))
      

      【讨论】:

        【解决方案4】:

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

        【讨论】:

        • 另请注意,它的工作量是迭代解决方案的两倍,因为它必须扫描列表两次
        【解决方案5】:

        怎么样:

        (defun butlast2 (L)
          (if (null (rest L))
            nil
            (cons (first L) (butlast2 (rest L)))
          )
        )
        

        【讨论】:

        • 在更长的列表上炸毁堆栈,并且不是尾递归,因此即使尾调用优化也无济于事。
        【解决方案6】:
        (defun remove-last (lst)
          (do ((l lst (rest l))
               (res '()))
              ((null (rest l)) (nreverse res))
            (push (first l) res)))
        

        【讨论】:

        • 您可以将res绑定与do表单的绑定形式。
        • @Svante,很好,我没想到。
        猜你喜欢
        • 2023-04-02
        • 2016-01-30
        • 1970-01-01
        • 1970-01-01
        • 2013-04-30
        • 1970-01-01
        • 1970-01-01
        • 2017-05-11
        • 1970-01-01
        相关资源
        最近更新 更多