I'm pretty fresh to the Common Lisp scene and I can't seem to find an quick way to get the nth element from a list and remove it from said list at the same time. I've done it, but it ain't pretty, what I'd really like is something like "pop" but took a second parameter:
(setf x '(a b c d))
(setf y (popnth 2 x))
; x is '(a b d)
; y is 'c
I'm pretty sure that "popnth" would have to be a macro, in case the parameter was 0 and it had to behave like "pop".
EDIT: Here's my crap first version:
(defmacro popnth (n lst)
(let ((tempvar (gensym)))
`(if (eql ,n 0)
(pop ,lst)
(let ((,tempvar (nth ,n ,lst)))
(setf (cdr (nthcdr ,(- n 1) ,lst)) (nthcdr ,(+ n 1) ,lst))
,tempvar))))
I have same suspicion as @6502...If I remember right...Neither
push
norpop
can be defined as modify-macros, the former because theplace
is not its first argument, and the latter because its return value is not the modified object.Definition of
define-modify-macro
An expression of the form
(define-modify-macro m (p1 ... pn) f)
defines a new macrom
, such that a call of the form(m place a1 ... an)
will causeplace
to be set to(f val a1 ... an)
, whereval
represents the value ofplace
. The parameters may also include rest and optional parameters. The string, if present, becomes the documentation of the new macro.I have this
popnth
works just fine:I came up with a solution that is a little more efficient than my first attempt:
Here is it in action:
Something like this:
Removing the nth element of a list:
constantly returns a function, that always returns its argument.
As a macro that accepts a place, using define-modify-macro:
POP-NTH
Example:
Yes, Lisp has a macro for popping the N-th element of a list: it is called
pop
.pop
works with any form that denotes a place.The problem is that, unlike
cddr
,nthcdr
isn't an accessor; a form like(nthcdr 3 list)
does not denote a place; it works only as a function call.Writing a specialized form of
pop
is not the best answer; rather, we can achieve a more general fix by writing a clone ofnthcdr
which behaves like a place accessor. Then thepop
macro will work, and so will every other macro that works with places likesetf
androtatef
.Test:
A possible improvement to the implementation is to analyze the
idx
form and optimize away the generated code that implements the run-time check on the value ofidx
. That is to say, ifidx
is a constant expression, there is no need to emit the code which tests whetheridx
is zero. The appropriate code variant can just be emitted. Not only that, but for small values ofidx
, the code can emit special variants based on the "cadavers":cddr
,cffffdr
, rather than the generalnthcdr
. However, some of these optimizations might be done by the Lisp compiler and thus redundant.