Lisp: Elegant way to remove trailing zero from a list? (Overview)

I want to write a function that removes trailing zero from a list. At first I tried to write it with recursion, but in the end:

(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 '(ab) (strip-tail '(ab nil nil)))) (assert (equal '(a nil b) (strip-tail '(a nil b nil)))) (assert (equal '(ab) (strip-tail '(ab)))) 

This may be clear, but I'm not sure. Is there a lazier way to do this?

+4
source share
6 answers

Well, the version will be:

  • cancel list
  • remove leading nils
  • cancel list

Code:

 (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)) 

Here is another option:

  • iterate over the list and mark the position of the first zero, followed only by nils
  • return subsequence

code:

 (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 pi) else when (and pe) do (setf p nil)) (if p (subseq list 0 p) list)) 
+3
source
 (defun strip-tail (ls) (labels ((strip-car (l) (cond ((null l) nil) ((null (car l)) (strip-car (cdr l))) (tl)))) (reverse (strip-car (reverse ls))))) 

Run example (against your test cases):

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

How about this?

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

... I wonder how to make it tail recursive, although this version would have exhausted the stack for large lists.

+2
source

Here's what I came up with, assuming that you are not opposed to being destructive:

 (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) -> (ABC NIL DE NIL NIL F) 
+1
source

I tried using recursion, but it does not work in GNU CL:

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

The idea is this:

  • check if the last element of the list is zero, if so make a recursive call with the last element deleted ( butlast )
  • then return the list itself
0
source

Well, actually this is not the answer, but I thought that I would put it here too so that it would have better visibility.

In your initial implementation, do you think that non-list items should be processed?

 * (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. 
-1
source

All Articles