Hey:
Here's a destructive/non-consing version of DELETE-FROM-PLIST. I've
tested with (I think) all corner cases from the REPL, but I ought to
write tests proper.
The function is here http://tinyurl.com/adqkssu ;or the following huge
link, in case the last one is invalidated
https://bitbucket.org/tarballs_are_good/lisp-random/src/3db634111d35e788c6e…
.
For simplicity or ease of review from an email client, I've pasted the
function at the end of this email.
Additionally, this function would make it pretty easy to write
DELETE-FROM-PLIST-IF{-NOT}, since the function to determine if a key
is bad is factored out. If one did write this function, then it would
be easy to define DELETE-FROM-PLIST in terms of it.
Let me know if there are any changes that should be made.
Cheers,
Robert Smith
;;;; from delete-from-plist.lisp
(defun delete-from-plist (plist &rest keys)
"Delete all keys and pairs indicated by KEYS from the plist PLIST."
(labels ((assert-proper-plist (x)
(assert x () "Expected a proper plist, got ~S" plist))
(bad-key-p (key)
(member key keys :test #'eq))
(find-first ()
"Find the first cons in PLIST to keep."
(loop :for the-cons :on plist :by #'cddr
:unless (prog1 (bad-key-p (car the-cons))
(assert-proper-plist (cdr the-cons)))
:do (return the-cons)
:finally (return nil))))
(declare (inline assert-proper-plist
bad-key-p
find-first))
;; Find the first good key and delete any bad key-value pairs
;; between it and the start.
(let ((first (find-first)))
(unless (eq first plist)
(setf (cddr plist)
first))
;; At this point, we know FIRST points to the first key
;; which exists, or NIL.
(loop :with last-good := first ; Keep the last good key
:for the-cons :on (cddr first) :by #'cddr
:do (progn
(assert-proper-plist (cdr the-cons))
(if (bad-key-p (car the-cons))
(setf (cddr last-good)
(cddr the-cons))
(setf last-good the-cons)))
:finally (return first)))))