Lisp:从列表中删除尾随nil的优雅方式? (评论)

时间:2009-07-09 22:32:57

标签: coding-style lisp common-lisp

我想编写一个从列表中删除尾随nil的函数。我首先尝试用递归优雅地编写它,但结果是这样的:

(defun strip-tail (lst)
  (let ((last-item-pos (position-if-not #'null lst :from-end t)))
    (if last-item-pos
      (subseq lst 0 (1+ last-item-pos)))))

; Test cases.
(assert (eq nil (strip-tail nil)))
(assert (eq nil (strip-tail '(nil))))
(assert (equal '(a b) (strip-tail '(a b nil nil))))
(assert (equal '(a nil b) (strip-tail '(a nil b nil))))
(assert (equal '(a b) (strip-tail '(a b))))

可以说很清楚,但我不相信。是否有一种更为疯狂的方式呢?

6 个答案:

答案 0 :(得分:3)

嗯,版本是:

  1. 反向列表
  2. 删除前导
  3. 反向列表
  4. 代码:

    (defun list-right-trim (list &optional item)
      (setf list (reverse list))
      (loop for e in list while (eq item e) do (pop list))
      (reverse list))
    

    这是另一种变体:

    1. 遍历列表并记下第一个nil的位置,后面只有nils
    2. 返回子序列
    3. 代码:

      (defun list-right-trim (list &aux (p nil))
        (loop for i from 0 and e in list
          when (and (null p) (null e)) 
          do (setf p i)
          else when (and p e) do (setf p nil))
        (if p (subseq list 0 p) list))
      

答案 1 :(得分:3)

(defun strip-tail (ls)
    (labels ((strip-car (l)
                  (cond ((null l)       nil)
                        ((null (car l)) (strip-car (cdr l)))
                        (t              l))))
        (reverse (strip-car (reverse ls)))))

示例运行(针对您的测试用例):

[1]> (assert (eq nil (strip-tail nil)))
NIL
[2]> (assert (eq nil (strip-tail '(nil)))) ;'
NIL
[3]> (assert (equal '(a b) (strip-tail '(a b nil nil))))
NIL
[4]> (assert (equal '(a nil b) (strip-tail '(a nil b nil))))
NIL
[5]> (assert (equal '(a b) (strip-tail '(a b))))
NIL
[6]> 

答案 2 :(得分:2)

这个怎么样?

(defun strip-tail (lst)
  (if lst
    (let ((lst (cons (car lst) (strip-tail (cdr lst)))))
      (if (not (equal '(nil) lst)) lst))))

...想知道如何使其尾递归,但这个版本会耗尽大型列表的堆栈。

答案 3 :(得分:1)

这是我想出的,假设你不介意这是破坏性的:

(defvar foo (list 'a 'b 'c nil 'd 'e 'nil 'nil 'f nil nil))

(defun get-last-non-nil (list &optional last-seen)
  (if list
      (if (car list)
          (get-last-non-nil (cdr list) list)
          (get-last-non-nil (cdr list) last-seen))
      last-seen))

(defun strip-tail (list)
  (let ((end (get-last-non-nil list)))
    (if (consp end)
        (when (car end) (setf (cdr end) nil) list))))

(strip-tail foo) -> (A B C NIL D E NIL NIL F)

答案 4 :(得分:0)

我尝试使用递归但它在GNU CL上不起作用:

(defun strip(lst) 
    (if (null (last lst))
        (strip (butlast lst))            
     lst))

这个想法是:

  1. 测试最后一个列表元素是否为nil,如果是,则进行递归调用  删除最后一个元素( butlast
  2. 然后返回列表本身

答案 5 :(得分:-1)

嗯,这不是一个真正的答案,但我想我也把它放在这里,所以它有更好的可见性。

在您的原始实现中,您认为应该处理非列表项吗?

* (strip-tail "abcde")

"abcde"
* (strip-tail 42)

debugger invoked on a TYPE-ERROR in thread #<THREAD "initial thread" {A69E781}>:
  The value 42 is not of type SEQUENCE.
相关问题