Search code examples
common-lisp

Removing last two elements from a list in Lisp


I need to remove the last two elements from a list in common list, but I can remove only one. What's the way?

(defun my-butlast (list)
        (loop for l on list
              while (rest l)
              collect (first l)))

Solution

  • Simple: reverse, pop, pop, reverse ;-) 1

    More efficiently, the following works too:

    (let ((list '(a b c d)))
      (loop 
        for x in list
        for y in (cddr list)
        collect x))
    

    This can also be written, for some arbitrary L and N:

    (mapcar #'values L (nthcdr N L)) 
    

    It works because iteration over multiple lists is bounded by the shortest one. What matters here is the length of the second list (we don't care about its values), which is the length of the original list minus N, which must be a non-negative integer. Notice that NTHCDR conveniently works with sizes greater than the length of the list given in argument.

    With the second example, I use the VALUES function as a generalized identity function; MAPCAR only uses the primary value of the computed values, so this works as desired. The behavior is consistent with the actual BUTLAST2 function, which returns nil for N larger than the number of elements in the list. The actual BUTLAST function can also deal with improper (dotted) lists, but the above version cannot.


    1. (alexandria:compose #'nreverse #'cddr #'reverse)

    2. BUTLAST is specified as being equivalent to (ldiff list (last list n)). I completely forgot about the existence of LDIFF !