From: ·············@gmail.com
Subject: (setf (getf ... and list of plists
Date: 
Message-ID: <1185588680.100091.145410@q75g2000hsh.googlegroups.com>
I am still working through Ch. 3 of the Practical CL book (but Potter
will take precedence this weekend), applying it to my database of
japanese phrases and their english translations.  I made progress to
the update function whose purpose is to update the contents of a
database record.

In my setup (clisp), I have defined this function as (this is pretty
much a copy of Seibel's function definition, except that I have
commented out the (setf *db* ...) form:

(defun update-entry (selector-fn &key english japanese)
;;(setf *db*
  (mapcar
   #'(lambda (this-entry)
       ;; when this entry matches selector-fn ...
       (when (funcall selector-fn this-entry)
	 ;; ... modify appropriate entries ...
	 (if english  (setf (getf this-entry :english)  english ))
	 (if japanese (setf (getf this-entry :japanese) japanese)))
       ;; ... and return this entry
       this-entry)
   *db*))
;;)

I have commented out the form because I did not want to mess-up *db*
until I understand what is going on.

But it turned out, that *db* is modified even without the (setf
*db* ...) form.  I found a discussion started by Peter  from a few
years ago (subject is "(setf (getf ...))"  ) from which I gather that
the "(setf (getf  ..." construct can (but does not have to) actually
modify the contents of *db* _in place_.

Is that correct?  And is the standard unclear on it, and that is why
Peter used the (setf *db* ...) form to make sure that the
modifications are stored in *db*?

Thanks for clarifying this,

Mirko

From: Kent M Pitman
Subject: Re: (setf (getf ... and list of plists
Date: 
Message-ID: <uy7h15aee.fsf@nhplace.com>
·············@gmail.com writes:

> I am still working through Ch. 3 of the Practical CL book (but Potter
> will take precedence this weekend), applying it to my database of
> japanese phrases and their english translations.  I made progress to
> the update function whose purpose is to update the contents of a
> database record.
> 
> In my setup (clisp), I have defined this function as (this is pretty
> much a copy of Seibel's function definition, except that I have
> commented out the (setf *db* ...) form:
> 
> (defun update-entry (selector-fn &key english japanese)
> ;;(setf *db*
>   (mapcar
>    #'(lambda (this-entry)
>        ;; when this entry matches selector-fn ...
>        (when (funcall selector-fn this-entry)
>        ;; ... modify appropriate entries ...
>        (if english  (setf (getf this-entry :english)  english ))
>        (if japanese (setf (getf this-entry :japanese) japanese)))
>        ;; ... and return this entry
>        this-entry)
>    *db*))
> ;;)
> 
> I have commented out the form because I did not want to mess-up *db*
> until I understand what is going on.
> 
> But it turned out, that *db* is modified even without the (setf
> *db* ...) form.  I found a discussion started by Peter  from a few
> years ago (subject is "(setf (getf ...))"  ) from which I gather that
> the "(setf (getf  ..." construct can (but does not have to) actually
> modify the contents of *db* _in place_.
> 
> Is that correct?  And is the standard unclear on it, and that is why
> Peter used the (setf *db* ...) form to make sure that the
> modifications are stored in *db*?

No.  The standard is not unclear.  Approximately what happens is:

If there is an extant cell for the given
indicator on the plist, SETF of GETF is able to just set the cadr of the plist
after the indicator.  That is, if you have a plist in *db* like (A B C D)
and you use SETF of GETF on A, then you can just change the location holding
B to a new value.  But if a new cell must be created, you have to have 
somewhere to put it. You might think it should go at the end, but it isn't
defined this way.  Rather, it's approximately defined [it doesn't do it
this way, but it will be easiest to understand this way, I think] as:

 (defmacro setf-of-getf (place indicator value) ;APPROXIMATE ONLY
   `(let ((place-value ,place)
          (indicator ,indicator)
          (value ,value))
      (loop for (ind . tail) on place-value
            when (eq ind indicator)
              do (return (setf (car tail) value))
            finally 
              (progn (setf ,place (list* indicator value place-value))))))

Note that there are some issues of double-evaluation and some issues
of name clashes that the above does not resolve.  But the point is
that in the case where the cell is not present, you must assign the
place.  You might think you could NCONC the value onto the end of the
plist, but you'd be wrong if the plist were empty.  So it isn't
defined that way.

For a really proper explanation of this, see the HyperSpec on the
issue of places.  The above is just to give you a sense of the "why"
the definition involves setting the place, but not the "how".  The
"how" is done quite a bit differently for reasons of robustness/generality.

I think if you modify the mapcar to use mapl instead, and instead of 
this-entry, you call the arg 

(defparameter *my-db* (list (list :dimensions '(2 2))
                            (list :dimensions '(3 4))))

(defun my-update-entry (selector-fn &key english japanese)
   (mapl #'(lambda (loc)
             ;; when this entry matches selector-fn ...
             (when (funcall selector-fn (car loc))
               ;; ... modify appropriate entries ...
              (if english  (setf (getf (car loc) :english)  english ))
              (if japanese (setf (getf (car loc) :japanese) japanese)))
             ;; ... and return this entry
            (car loc))
        *my-db*))

(my-update-entry #'(lambda (x)
                     (destructuring-bind (len wid) (getf x :dimensions)
                       (and len wid (= len wid))))
                :english 'square :japanese 'hunoz)
=> ((:JAPANESE HUNOZ :ENGLISH SQUARE :DIMENSIONS (2 2))
    (:DIMENSIONS (3 4)))

(my-update-entry #'(lambda (x)
                     (destructuring-bind (len wid) (getf x :dimensions)
                       (and len wid (not (= len wid)))))
                 :english 'rectangle
                 :japanese 'dunno-either)

=> ((:JAPANESE HUNOZ :ENGLISH SQUARE :DIMENSIONS (2 2))
    (:JAPANESE DUNNO-EITHER :ENGLISH RECTANGLE :DIMENSIONS (3 4)))

(my-update-entry #'(lambda (x) 
                     (destructuring-bind (len wid) (getf x :dimensions)
                       (and len wid (not (= len wid)))))
                :english 'shoebox)

=> ((:JAPANESE HUNOZ :ENGLISH SQUARE :DIMENSIONS (2 2))
    (:JAPANESE DUNNO-EITHER :ENGLISH SHOEBOX :DIMENSIONS (3 4)))

If you don't want to be updating *db*, some appropriate calls to copy-list
should fix that.  I'll assume you can work that out and that your problem
is that you want the updates done consistently.

Does this help?

p.s. In case you're curious, HUNOZ is a nonsense word Steele used to use in
     some of his early articles for a value that is useless and intended to
     be ignored.  If you read it as WHO-KNOWS, it will probably help.
     It wasn't an attempt at Japanese.  I don't know any and was just trying
     to supply sample values so you could see your program updating.

p.p.s. Try not to use tabs in your usenet posts since they change where 
       they tab to when indentation marks like "> " are added.
       Also, the width of a tab is not globally agreed upon.
       Use spaces if possible.
From: Rob Warnock
Subject: Re: (setf (getf ... and list of plists
Date: 
Message-ID: <xOOdnbyHGNxaZjfbnZ2dnUVZ_jqdnZ2d@speakeasy.net>
Kent M Pitman  <······@nhplace.com> wrote:
+---------------
| [(SETF (GETF ...) ...) is ] approximately defined [it doesn't do it
| this way, but it will be easiest to understand this way, I think] as:
|  (defmacro setf-of-getf (place indicator value) ;APPROXIMATE ONLY
|    `(let ((place-value ,place)
|           (indicator ,indicator)
|           (value ,value))
|       (loop for (ind . tail) on place-value
|             when (eq ind indicator)
|               do (return (setf (car tail) value))
|             finally 
|               (progn (setf ,place (list* indicator value place-value))))))
+---------------

Uh... shouldn't that LOOP/FOR clause have a BY #'CDDR on it? That is:

        (loop for (ind . tail) on place-value by #'cddr
	      when (eq ind indicator)
		...
	      ... )

Yes, I know you said "APPROXIMATE ONLY", but if he tries to manually
walk it through, OP might get confused without the BY #'CDDR.


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Kent M Pitman
Subject: Re: (setf (getf ... and list of plists
Date: 
Message-ID: <u6444tzm1.fsf@nhplace.com>
····@rpw3.org (Rob Warnock) writes:

> Kent M Pitman  <······@nhplace.com> wrote:
> +---------------
> | [(SETF (GETF ...) ...) is ] approximately defined [it doesn't do it
> | this way, but it will be easiest to understand this way, I think] as:
> |  (defmacro setf-of-getf (place indicator value) ;APPROXIMATE ONLY
> |    `(let ((place-value ,place)
> |           (indicator ,indicator)
> |           (value ,value))
> |       (loop for (ind . tail) on place-value
> |             when (eq ind indicator)
> |               do (return (setf (car tail) value))
> |             finally 
> |               (progn (setf ,place (list* indicator value place-value))))))
> +---------------
> 
> Uh... shouldn't that LOOP/FOR clause have a BY #'CDDR on it? That is:
> 
>         (loop for (ind . tail) on place-value by #'cddr
> 	      when (eq ind indicator)
> 		...
> 	      ... )
> 
> Yes, I know you said "APPROXIMATE ONLY", but if he tries to manually
> walk it through, OP might get confused without the BY #'CDDR.

Well, you perhaps missed an important detail earlier in the header, 
or I forgot to highlight it in my comment, but let me emphasize it here:

   Date: 28 Jul 2007 02:07:21 -0400 (up way too late)

And I forgot to say "untested" as well.  Though testing approximate code
is problematic.

Anyway, thanks for spotting that, Rob. :)
From: ·············@gmail.com
Subject: Re: (setf (getf ... and list of plists
Date: 
Message-ID: <1185670428.015431.184870@m37g2000prh.googlegroups.com>
On Jul 28, 2:07 am, Kent M Pitman <······@nhplace.com> wrote:
> ·············@gmail.com writes:
> > I am still working through Ch. 3 of the Practical CL book (but Potter
> > will take precedence this weekend), applying it to my database of
> > japanese phrases and their english translations.  I made progress to
> > the update function whose purpose is to update the contents of a
> > database record.
>
> > In my setup (clisp), I have defined this function as (this is pretty
> > much a copy of Seibel's function definition, except that I have
> > commented out the (setf *db* ...) form:
>
> > (defun update-entry (selector-fn &key english japanese)
> > ;;(setf *db*
> >   (mapcar
> >    #'(lambda (this-entry)
> >        ;; when this entry matches selector-fn ...
> >        (when (funcall selector-fn this-entry)
> >        ;; ... modify appropriate entries ...
> >        (if english  (setf (getf this-entry :english)  english ))
> >        (if japanese (setf (getf this-entry :japanese) japanese)))
> >        ;; ... and return this entry
> >        this-entry)
> >    *db*))
> > ;;)
>
> > I have commented out the form because I did not want to mess-up *db*
> > until I understand what is going on.
>
> > But it turned out, that *db* is modified even without the (setf
> > *db* ...) form.  I found a discussion started by Peter  from a few
> > years ago (subject is "(setf (getf ...))"  ) from which I gather that
> > the "(setf (getf  ..." construct can (but does not have to) actually
> > modify the contents of *db* _in place_.
>
> > Is that correct?  And is the standard unclear on it, and that is why
> > Peter used the (setf *db* ...) form to make sure that the
> > modifications are stored in *db*?
>
> No.  The standard is not unclear.  Approximately what happens is:
>
> If there is an extant cell for the given
> indicator on the plist, SETF of GETF is able to just set the cadr of the plist
> after the indicator.  That is, if you have a plist in *db* like (A B C D)
> and you use SETF of GETF on A, then you can just change the location holding
> B to a new value.

Since I am only at Ch. 3 of Practical CL, I will take this to say that
the
(setf (getf ...)) modifies the list in place, if we are talking about
a
"simple" change (where "simple" means replace one value with another).

It will take me a bit more time to digest the rest of your message :-)

...  stuff deleted ...


>
> p.p.s. Try not to use tabs in your usenet posts since they change where
>        they tab to when indentation marks like "> " are added.
>        Also, the width of a tab is not globally agreed upon.
>        Use spaces if possible.

Hmm, I'm not sure where the tabs came from.  I pasted the code from
emacs.  I'll
check next time.

Thanks,

Mirko