I go through periods of obsession with different programming languages. I don't know why, I guess I get bored easily. I usually get stuck using the same 2 or 3 at my day job. Since I work in web development, that tends to be PHP, Python and JavaScript. When I hack for fun, I'm not very goal-oriented, and I tend to want to explore and try new and exciting things. Well, new for me at least. Lately, I've been on a Common Lisp kick again. I've been writing code. I got to a point where I wanted to be able to pop the "nth" element from a list. Strangely, this doesn't seem to exist in Common Lisp. In Python, the "pop" method on lists has an optional parameter for which element to remove and return:
x = [1,2,3,4] y = x.pop(1) print(x) # [1,3,4] print(y) # 2
So I had to write one, which involved learning how macros work again. Here's my first attempt:
(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))))
Unfortunately, that makes 1 call to "nth" and 2 calls to "nthcdr" making the whole thing probably a bit slower than necessary for larger lists. So here is a version that only iterates through the list once:
(defmacro popnth (n lst)
(let ((t1 (gensym))(t2 (gensym)))
`(if (eql ,n 0)
(pop ,lst)
(let* ((,t1 (nthcdr ,(- n 1) ,lst))
(,t2 (car (cdr ,t1))))
(setf (cdr ,t1) (cddr ,t1))
,t2))))
Of course, there's a subtle error in these examples, the unquoting is off in the subtraction. Here it is fixed:
(defmacro popnth (n lst)
(let ((t1 (gensym))(t2 (gensym)))
`(if (eql ,n 0)
(pop ,lst)
(let* ((,t1 (nthcdr (- ,n 1) ,lst))
(,t2 (car (cdr ,t1))))
(setf (cdr ,t1) (cddr ,t1))
,t2))))

