[alexandria-devel] Implementation of DELETE-FROM-PLIST

James M. Lawrence llmjjmll at gmail.com
Sun Feb 24 14:30:18 UTC 2013


On Sat, Feb 23, 2013 at 4:09 AM, Robert Smith <quad at symbo1ics.com> wrote:
>
> (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)))))

Using two loops seems awkward to me. How about one?

(defun delete-from-plist (plist &rest keys)
  (loop with head = plist
        with tail = nil
        for (key . rest) on plist by #'cddr
        do (assert rest () "Expected a proper plist, got ~S" plist)
           (if (member key keys :test #'eq)
               (let ((next (cdr rest)))
                 (if tail
                     (setf (cdr tail) next)
                     (setf head next)))
               (setf tail rest))
        finally (return head)))




More information about the alexandria-devel mailing list