While CL has a concept of improper (dotted or circular) lists, very few standard functions support them. Today, while tinkering with my NIH universal let, I found myself in need of a mapcar equivalent to subst to call on a destructuring lambda list which can be dotted.
Here's a simple and relatively efficient version (needs a basic queue structure since loop can't collect in finally and doesn't give access to the raw tail cons cell anyway):
(defun make-queue (&rest initial-contents)
(let ((queue (cons nil (copy-list initial-contents))))
(setf (car queue) (last queue))
queue))
(declaim (inline push-queue))
(defun push-queue (obj queue)
(let ((new-tail (list obj)))
(setf (cdar queue) new-tail
(car queue) new-tail)))
(defun mapcar-dotted (fun list)
(let ((queue (make-queue)))
(do ((cell list (cdr cell)))
((atom cell) (progn
(when cell
(setf (cdar queue) (funcall fun cell)))
(cdr queue)))
(push-queue (funcall fun (car cell)) queue))))
Basic test:
(format t "~@{~A~%~}"
(mapcar-dotted #'identity nil)
(mapcar-dotted #'identity '(1))
(mapcar-dotted #'identity '(1 2 3))
(mapcar-dotted #'identity '(1 2 . 3)))
;; NIL
;; (1)
;; (1 2 3)
;; (1 2 . 3)
While CL has a concept of improper (dotted or circular) lists, very few standard functions support them. Today, while tinkering with my NIH universal let, I found myself in need of a mapcar equivalent to subst to call on a destructuring lambda list which can be dotted.
Here's a simple and relatively efficient version (needs a basic queue structure since
loopcan'tcollectinfinallyand doesn't give access to the raw tail cons cell anyway):Basic test: