I want to write a function that removes trailing nil's from a list. I first tried to write it elegantly with recursion, but ended up like this:
(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))))
It's arguably clear, but I'm not convinced. Is there a more lispy way to do it?
Well, a version would be:
The 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 variant:
the 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 p i)
else when (and p e) do (setf p nil))
(if p (subseq list 0 p) list))
(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)))))
Sample run (against your test cases):
[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]>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With