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)))
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 BUTLAST
2 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
!