Re: [alexandria-devel] Implementation of DELETE-FROM-PLIST
pushed, thanks! and sorry for the delay! -- • attila lendvai • PGP: 963F 5D5F 45C7 DFCD 0A39 -- “It ain’t what you don’t know that gets you in trouble. It’s what you know for sure that ain’t so.” — Mark Twain (1835–1910)
Re: [alexandria-devel] Implementation of DELETE-FROM-PLIST
Apologies to Robert for stealing his thunder. 0001-make-REMOVE-FROM-PLIST-non-consing.patch Description: Binary data 0002-fix-test-PARSE-ORDINARY-LAMBDA-LIST.1.patch Description: Binary data ___ alexandria-devel mailing list alexandria-devel@common-lisp.net http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Re: [alexandria-devel] Implementation of DELETE-FROM-PLIST
James M. Lawrence llmjj...@gmail.com writes: 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))) I mention this for completeness and novelty, not for suitability: (defun sans (plist rest keys) (let ((sans ())) (loop (let ((tail (nth-value 2 (get-properties plist keys ;; this is how it ends (unless tail (return (nreconc sans plist))) ;; copy all the unmatched keys (loop until (eq plist tail) do (push (pop plist) sans) (push (pop plist) sans)) ;; skip the matched key (setq plist (cddr plist)) I don't think I've seen GET-PROPERTIES and NRECONC outside this function. I got it from here: http://xach.com/naggum/articles/3247672165664225%40naggum.no.html Zach ___ alexandria-devel mailing list alexandria-devel@common-lisp.net http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Re: [alexandria-devel] Implementation of DELETE-FROM-PLIST
Zach Beane x...@xach.com writes: James M. Lawrence llmjj...@gmail.com writes: 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))) I mention this for completeness and novelty, not for suitability: (defun sans (plist rest keys) (let ((sans ())) (loop (let ((tail (nth-value 2 (get-properties plist keys ;; this is how it ends (unless tail (return (nreconc sans plist))) ;; copy all the unmatched keys (loop until (eq plist tail) do (push (pop plist) sans) (push (pop plist) sans)) ;; skip the matched key (setq plist (cddr plist)) I don't think I've seen GET-PROPERTIES and NRECONC outside this function. I got it from here: http://xach.com/naggum/articles/3247672165664225%40naggum.no.html If striving for shortness: (defun delete-from-plist (plist rest keys) (dolist (key keys plist) (remf plist key))) -- With best regards, Stas. ___ alexandria-devel mailing list alexandria-devel@common-lisp.net http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Re: [alexandria-devel] Implementation of DELETE-FROM-PLIST
Looks good! Much cleaner/better. -Robert On Sun, Feb 24, 2013 at 6:30 AM, James M. Lawrence llmjj...@gmail.com wrote: On Sat, Feb 23, 2013 at 4:09 AM, Robert Smith q...@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))) ___ alexandria-devel mailing list alexandria-devel@common-lisp.net http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel ___ alexandria-devel mailing list alexandria-devel@common-lisp.net http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Re: [alexandria-devel] Implementation of DELETE-FROM-PLIST
How is (:A 1 :D 4) wrong? There is no way to get it to just (:D 4) via mutation when passing to a function. If we want to have just (:D 4), we will need to either pass in (nil nil plist) which will give (nil nil :D 4) or we will need to encapsulate or box the value before going in, by using something like https://bitbucket.org/tarballs_are_good/cl-ref . All in all, it still returns (:D 4), but it just modifies as much of the list structure as it can. -Robert On Sat, Feb 23, 2013 at 11:53 AM, Stas Boukarev stass...@gmail.com wrote: Robert Smith q...@symbo1ics.com writes: Good style remarks, I suppose, which I can change. On Sat, Feb 23, 2013 at 8:08 AM, Stas Boukarev stass...@gmail.com wrote: And I don't quite understand the purpose of (unless (eq first plist) (setf (cddr plist) first)) The point of that was for more DWIMness. Without it, we have: CL-USER (let ((x (list :a 1 :b 2 :c 3 :d 4))) (print (delete-from-plist x :a :b :c)) x) (:D 4) (:A 1 :B 2 :C 3 :D 4) And with it, we have: CL-USER (let ((x (list :a 1 :b 2 :c 3 :d 4))) (print (delete-from-plist x :a :b :c)) x) (:D 4) (:A 1 :D 4) If your typical mode of operation is to (setf x (delete-from...)), then of course either are fine. If your goal is to use DELETE-FROM-PLIST for its side effects, then I think the latter is more useful. In fact, for really simple and efficient imperative maintenance of a plist, just prepend (nil nil), and use the function purely for mutation. I don't see how (:A 1 :D 4) is useful, it's less wrong, but it's still wrong. -- With best regards, Stas. ___ alexandria-devel mailing list alexandria-devel@common-lisp.net http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Re: [alexandria-devel] Implementation of DELETE-FROM-PLIST
On Sat, Feb 23, 2013 at 4:19 PM, Stas Boukarev stass...@gmail.com wrote: It's wrong because it's completely useless, why would anyone use delete-from-plist without using the value returned by it, if the original list it modifies has the wrong result? Having to prepend two NILs is just bogus. 1. Because some people like or prefer to modify data structures (especially when you have elaborate data structures), and not bindings. 2. Having to prepend two NILs is fine I think. Yes it is hacky, but I don't see any inherent issue with it. It just establishes a (few) conses that act as the head or entry point to the list. Although, that's not true that there's no way to have (:D 4), but the problem just shifts to when it deletes everything and it's a NIL: (unless (eq first plist) (psetf (car plist) (car first) (cdr plist) (cdr first))) I suppose you're correct here. I was implicitly assuming that we would want to never modify the CAR, but that is a sort of useless assumption. But that part shouldn't be in alexandria (or any sane library, for that matter) either way, because it encourages erroneous usage, seemingly doing the right thing, but breaks when it comes to returning NIL. I don't think it's erroneous. We aren't conflating the ideas of modifying a data structure and modifying a binding. By not doing that extra mutation, we rely on the user to finish the job by re-setting their variable to the new value. The only thing weird about it is that there are a lot of Lisp functions which don't completely mutate the data structure, and expect the user to modify the binding. As far as I can tell, then, the really only argument against such a thing is that's not how other CL functions work, so for consistency's sake, we shouldn't either. Unless I'm missing some point. And there's alexandria:delete-from-plistf for people who are afraid of an extra SETF. In this, we are trading a purely functional (i.e., non-special/macro) solution for a macro solution. Doesn't that go against the grain of the prevalent ideology of Lisp? Cheers, Robert ___ alexandria-devel mailing list alexandria-devel@common-lisp.net http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Re: [alexandria-devel] Implementation of DELETE-FROM-PLIST
Robert Smith q...@symbo1ics.com writes: On Sat, Feb 23, 2013 at 4:19 PM, Stas Boukarev stass...@gmail.com wrote: It's wrong because it's completely useless, why would anyone use delete-from-plist without using the value returned by it, if the original list it modifies has the wrong result? Having to prepend two NILs is just bogus. 1. Because some people like or prefer to modify data structures (especially when you have elaborate data structures), and not bindings. Well, those people should learn that it's not possible in general. Particularly with lists, because of NIL. 2. Having to prepend two NILs is fine I think. Yes it is hacky, but I don't see any inherent issue with it. It just establishes a (few) conses that act as the head or entry point to the list. What if the key you want to remove is NIL? But that part shouldn't be in alexandria (or any sane library, for that matter) either way, because it encourages erroneous usage, seemingly doing the right thing, but breaks when it comes to returning NIL. I don't think it's erroneous. We aren't conflating the ideas of modifying a data structure and modifying a binding. By not doing that extra mutation, we rely on the user to finish the job by re-setting their variable to the new value. There's no other way to obtain the correct results otherwise. Having it to work in 99% is worse than having it not to work at all, because people might forget about the remaining 1% case more easily. So you end up with doing extra work which has no use. The goal is simplicity, not having to memorize in which cases it's alright and which it's not. Just use the return value and be merry. And there's alexandria:delete-from-plistf for people who are afraid of an extra SETF. In this, we are trading a purely functional (i.e., non-special/macro) solution for a macro solution. Doesn't that go against the grain of the prevalent ideology of Lisp? There's no trading, delete-from-plistf is just a define-modify-macro for delete-from-plist. -- With best regards, Stas. ___ alexandria-devel mailing list alexandria-devel@common-lisp.net http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel