Robert Smith quad@symbo1ics.com writes:
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/3db634111d35e788c6ea... .
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)))))
Alexandria is not using :keyword style for LOOP. DO in loop has an implicit progn, no need for an explicit one. unless (prog1 ... (assert)) can be better expressed as do (assert) unless ... return cons and finally (return nil) is not needed.
And I don't quite understand the purpose of (unless (eq first plist) (setf (cddr plist) first))