So I just noticed something in the dictionary entry for GETF that I
must have overlooked before and wanted to check that my new
interpretation is correct. The entry says:
"setf of getf is permitted to either write the value of place
itself, or modify of any part, car or cdr, of the list structure
held by place."
From that I take it from that the following function will not
necessarily modify the plists contained in the list-of-plists passed
to it since the SETF might simply change the value of the local
variable plist.
(defun change-values (list-of-plists)
(dolist (plist list-of-plists)
(setf (getf plist :foo) (random 10))))
In practice (in ACL anyway) when the elements of list-of-plists are
plists that already have a :foo entry, it *does* modify them. But
unless I'm missing something, there's no reason to rely on that
behavior. So am I missing anything?
-Peter
--
Peter Seibel ·····@javamonkey.com
Lisp is the red pill. -- John Fraser, comp.lang.lisp
In article <··············@javamonkey.com>, Peter Seibel
<·····@javamonkey.com> wrote:
> So I just noticed something in the dictionary entry for GETF that I
> must have overlooked before and wanted to check that my new
> interpretation is correct. The entry says:
>
> "setf of getf is permitted to either write the value of place
> itself, or modify of any part, car or cdr, of the list structure
> held by place."
>
> From that I take it from that the following function will not
> necessarily modify the plists contained in the list-of-plists passed
> to it since the SETF might simply change the value of the local
> variable plist.
>
> (defun change-values (list-of-plists)
> (dolist (plist list-of-plists)
> (setf (getf plist :foo) (random 10))))
>
> In practice (in ACL anyway) when the elements of list-of-plists are
> plists that already have a :foo entry, it *does* modify them. But
> unless I'm missing something, there's no reason to rely on that
> behavior. So am I missing anything?
I interpret this to mean that e.g.:
(let ( (plist '(a b c d)) )
(setf (getf plist 'a) 'q)
plist)
is free to return either (a q c d) or (c d a q).
Or, perhaps more to the point:
(let ( (plist '(a b)) )
(setf (getf plist 'q) 'z)
plist)
could return either (a b q z) or (q z a b).
E.
·········@jpl.nasa.gov (Erann Gat) wrote in message news:<··························@k-137-79-50-101.jpl.nasa.gov>...
> I interpret this to mean that e.g.:
>
> (let ( (plist '(a b c d)) )
> (setf (getf plist 'a) 'q)
> plist)
>
> is free to return either (a q c d) or (c d a q).
It's also free to return:
bus error (core dumped)
shell$
or
error: cannot store Q into CAR of read-only cell (A B C D).
repl>
:)
·········@jpl.nasa.gov (Erann Gat) writes:
> In article <··············@javamonkey.com>, Peter Seibel
> <·····@javamonkey.com> wrote:
>
> > So I just noticed something in the dictionary entry for GETF that I
> > must have overlooked before and wanted to check that my new
> > interpretation is correct. The entry says:
> >
> > "setf of getf is permitted to either write the value of place
> > itself, or modify of any part, car or cdr, of the list structure
> > held by place."
> >
> > From that I take it from that the following function will not
> > necessarily modify the plists contained in the list-of-plists passed
> > to it since the SETF might simply change the value of the local
> > variable plist.
> >
> > (defun change-values (list-of-plists)
> > (dolist (plist list-of-plists)
> > (setf (getf plist :foo) (random 10))))
> >
> > In practice (in ACL anyway) when the elements of list-of-plists are
> > plists that already have a :foo entry, it *does* modify them. But
> > unless I'm missing something, there's no reason to rely on that
> > behavior. So am I missing anything?
>
> I interpret this to mean that e.g.:
>
> (let ( (plist '(a b c d)) )
> (setf (getf plist 'a) 'q)
> plist)
>
> is free to return either (a q c d) or (c d a q).
>
> Or, perhaps more to the point:
>
> (let ( (plist '(a b)) )
> (setf (getf plist 'q) 'z)
> plist)
>
> could return either (a b q z) or (q z a b).
Sure. But that's sort of orthogonal to my question. It's pretty clear
that the place (`plist' in this case) will have a correct value (i.e.
one that contains the correct value for the set indicator and all the
old indicator/value pairs after the SETF. But that value could be
created in any of a number of ways: by modifying the CAR of the cons
cell containing the current value (assuming the indicator is already
present); modifying the CDR of the cons cell preceeding the indicator
to splice in a new cons cell; modifying that same CDR to remove the
two cons cells containing the indicator and its value and then add two
new cons cell elsewhere in the plist; or even copying the whole plist
and changing the appropriate value while making the copy. Obviously
some of these may be "better" implementations by various metrics but I
think--now--that they would all be supported by the standard. Which
means I need to make some changes to Chapter 3 of my book which
incorrectly depends on a function similar to CHANGE-VALUES. Oops.
-Peter
--
Peter Seibel ·····@javamonkey.com
Lisp is the red pill. -- John Fraser, comp.lang.lisp
Peter Seibel <·····@javamonkey.com> wrote in message news:<··············@javamonkey.com>...
> So I just noticed something in the dictionary entry for GETF that I
> must have overlooked before and wanted to check that my new
> interpretation is correct. The entry says:
>
> "setf of getf is permitted to either write the value of place
> itself, or modify of any part, car or cdr, of the list structure
> held by place."
>
> From that I take it from that the following function will not
> necessarily modify the plists contained in the list-of-plists passed
> to it since the SETF might simply change the value of the local
> variable plist.
>
> (defun change-values (list-of-plists)
> (dolist (plist list-of-plists)
> (setf (getf plist :foo) (random 10))))
What if some of the plists are NIL? There is no way that the original
list-of-plists can be modified in that case since there is no cell to
boostrap from; a new plist will be constructed and bound to the local
variable.
> In practice (in ACL anyway) when the elements of list-of-plists are
> plists that already have a :foo entry, it *does* modify them. But
> unless I'm missing something, there's no reason to rely on that
> behavior. So am I missing anything?
No; the meaning of ``permitted'' is clear.
I think for this type of code, you really need a SYMBOL-MACROLET based
version of DOLIST which allows the list to be mutated via SETQ or SETF
of the iteration variable.
How about DOLISTF:
(defmacro dolistf ((sym list &optional result) &body forms)
(let ((sublist-sym (gensym "SUBLIST-")))
`(block nil
(maplist (lambda (,sublist-sym)
(symbol-macrolet ((,sym (car ,sublist-sym)))
,@forms))
,list)
,result)))
Now you can do funky stuff like:
(defvar *list* (list 1 2 3))
(dolistf (i *list* *list*) (incf i)) --> (2 3 4)
Now there is a truly imperative DOLIST that is understandable to Java,
C++, Perl and Python programmers. ;)
With this DOLISTF, your example works as intended, even if we start
with a list of empty plists:
(defvar *list-of-plists* (list () () ()))
(dolistf (plist *list-of-plists*)
(setf (getf plist :foo) (random 10)))
*list-of-plists* --> ((:FOO 5) (:FOO 4) (:FOO 7))
Oh yeah, and since this is implemented over MAPLIST, using a real
lexical variable, per-iteration lexical capture works right, too.
In article <··············@javamonkey.com>,
Peter Seibel <·····@javamonkey.com> wrote:
> So I just noticed something in the dictionary entry for GETF that I
> must have overlooked before and wanted to check that my new
> interpretation is correct. The entry says:
>
> "setf of getf is permitted to either write the value of place
> itself, or modify of any part, car or cdr, of the list structure
> held by place."
>
> From that I take it from that the following function will not
> necessarily modify the plists contained in the list-of-plists passed
> to it since the SETF might simply change the value of the local
> variable plist.
>
> (defun change-values (list-of-plists)
> (dolist (plist list-of-plists)
> (setf (getf plist :foo) (random 10))))
>
> In practice (in ACL anyway) when the elements of list-of-plists are
> plists that already have a :foo entry, it *does* modify them. But
> unless I'm missing something, there's no reason to rely on that
> behavior. So am I missing anything?
No, you've got it. It has to be able to assign the variable in case the
original value is NIL -- there's no cars or cdrs to modify.
--
Barry Margolin, ······@alum.mit.edu
Arlington, MA
Barry Margolin wrote:
> In article <··············@javamonkey.com>,
> Peter Seibel <·····@javamonkey.com> wrote:
>
>
> No, you've got it. It has to be able to assign the variable in case the
> original value is NIL -- there's no cars or cdrs to modify.
>
So the right way to do Peter's task is
(defun change-values (list-of-plists)
(repeat :for ((_ :in list-of-plists :tail pll))
(setf (getf (car pll) ':foo)
(random 10))))
The 'repeat' macro construct (v :in l :tail w) steps 'v' through list
'l', binding 'w' to successive tails (starting with 'v'). If 'v' isn't
referenced, it can be written as '_'. I assume there's a way to do the
same thing with 'loop'.
--
-- Drew McDermott
Yale Computer Science Department
Drew McDermott <··················@at.yale.dot.edu> writes:
> Barry Margolin wrote:
> > In article <··············@javamonkey.com>,
> > Peter Seibel <·····@javamonkey.com> wrote:
> >
> >
> > No, you've got it. It has to be able to assign the variable in case
> > the original value is NIL -- there's no cars or cdrs to modify.
> >
>
> So the right way to do Peter's task is
>
> (defun change-values (list-of-plists)
> (repeat :for ((_ :in list-of-plists :tail pll))
> (setf (getf (car pll) ':foo)
> (random 10))))
Aren't you still missing a SETF? I.e.
(setf (car pll) (setf (getf (car pll) :foo) (random 10)))
instead of:
(setf (getf (car pll) :foo) (random 10))
> The 'repeat' macro construct (v :in l :tail w) steps 'v' through list
> 'l', binding 'w' to successive tails (starting with 'v'). If 'v'
> isn't referenced, it can be written as '_'. I assume there's a way to
> do the same thing with 'loop'.
With LOOP I guess that would be:
(defun change-values (list-of-plists)
(loop for e on list-of-plists do
(setf (car e) (setf (getf (car e) :foo) (random 10)))))
Thanks for the insight, I hadn't thought of doing it that way.
-Peter
--
Peter Seibel ·····@javamonkey.com
Lisp is the red pill. -- John Fraser, comp.lang.lisp
>> [me]
>>So the right way to do Peter's task is
>>
>> (defun change-values (list-of-plists)
>> (repeat :for ((_ :in list-of-plists :tail pll))
>> (setf (getf (car pll) ':foo)
>> (random 10))))
>
> [Peter Seibel]
> Aren't you still missing a SETF? I.e.
>
> (setf (car pll) (setf (getf (car pll) :foo) (random 10)))
>
> instead of:
>
> (setf (getf (car pll) :foo) (random 10))
yt(4): (defvar pll1 '((:foo 0 :baz 0)
(:baz 0 :foo 0)
(:baz 0)))
pll1
yt(5): (change-values pll1)
nil
yt(6): pll1
((:foo 1 :baz 0) (:baz 0 :foo 0) (:foo 3 :baz 0))
yt(7):
The beauty of SETF is that it unravels its left-hand side in just the
right way.
You could write
(incf (getf (car pll) ':foo))
and the right thing would happen, too. (Although I'd rather write
(!= (get (car pll) ':foo) (+ *-* 1)), where != is like SETF except
that on the right-hand side '*-*' is bound to the value of the left-hand
side, again using the correct amount of unraveling so that nothing is
evaluated more than once.)
-- Drew McDermott
Peter Seibel wrote:
> (defun change-values (list-of-plists)
> (dolist (plist list-of-plists)
> (setf (getf plist :foo) (random 10))))
What's wrong with this?
(defun change-values (list-of-plists)
(mapcar (lambda (plist)
(list* :foo (random 10) plist))
list-of-plists))
and then:
(setf a-list-of-plists (change-values a-list-of-plists))
or additionally:
(define-modify-macro change-values-f ()
change-values)
so then:
(change-value-f a-list-of-plists)
If you need more efficiency, you can achieve it by using MAPCAN in
certain cases, and maybe provide another version of LIST* that modifies
an already existing :FOO property.
LIST* does the right thing because GETF is politically correct, i.e. it
prefers left entries over right entries. Since property list should
usually be short, the overhead shouldn't be too high. You could also
achieve a better average performance by only compressing property lists
that grow beyond a certain threshold.
The usual rule applies that you shouldn't prematurely optimize things
that you haven't profiled before.
All the best,
Pascal
--
Pascal Costanza University of Bonn
···············@web.de Institute of Computer Science III
http://www.pascalcostanza.de R�merstr. 164, D-53117 Bonn (Germany)