From: Jyri J. Virkki
Subject: PCL: question about lambda usage
Date: 
Message-ID: <11vsfm7kl0qtl1c@corp.supernews.com>
In PCL chapter 3,

(defun update (selector-fn &key title artist rating (ripped nil ripped-p))
  (setf *db*
        (mapcar
         #'(lambda (row)
             (when (funcall selector-fn row)
               (if title    (setf (getf row :title) title))
               (if artist   (setf (getf row :artist) artist))
               (if rating   (setf (getf row :rating) rating))
               (if ripped-p (setf (getf row :ripped) ripped)))
             row) *db*)))
             ^^^
Why the row parameter here to lamda? Taking it out compiles, but *db*
gets assigned a list of NILs.

I tried a simpler example to experiment:

(defvar *l* '(1 2 3 4 5))
(defun addl (x)
  (setf *l* (mapcar #'(lambda (num) (+ x num)) *l*)))

CL-USER> *l*
(1 2 3 4 5)
CL-USER> (addl 3)
(4 5 6 7 8)

So, addl version works the way I thought it would.  Adding a 3rd param
"num" to lambda in addl still compiles(!) but now addl does not modify
*l* (oddly, roughly opposite from the behavior of update).

Browsing ahead in the book a bit (Ch.5) it reiterates the form of lambda as:
(lambda (parameters) body)

So I'm confused. What's the 3rd param to lambda and why is it needed
in the update function example?

Thanks..

-- 
Jyri J. Virkki - Santa Cruz, CA

From: ············@gmail.com
Subject: Re: PCL: question about lambda usage
Date: 
Message-ID: <1140737033.257535.18000@e56g2000cwe.googlegroups.com>
The body is everything after the argument list, it can be several
forms. In the first example 'row' is not the "third parameter" to
lambda, it's part of the body. If you take it out, the function returns
the value of the (when ...) form instead of returning the updated row.

In the second example, (lambda (num) (+ x num)) adds x to its argument;
however (lambda (num) (+ x num) num) returns its argument unchanged!

Carlos
From: Kaz Kylheku
Subject: Re: PCL: question about lambda usage
Date: 
Message-ID: <1140739068.018683.197370@p10g2000cwp.googlegroups.com>
Jyri J. Virkki wrote:
> In PCL chapter 3,
>
> (defun update (selector-fn &key title artist rating (ripped nil ripped-p))
>   (setf *db*
>         (mapcar
>          #'(lambda (row)
>              (when (funcall selector-fn row)
>                (if title    (setf (getf row :title) title))
>                (if artist   (setf (getf row :artist) artist))
>                (if rating   (setf (getf row :rating) rating))
>                (if ripped-p (setf (getf row :ripped) ripped)))
>              row) *db*)))
>              ^^^
> Why the row parameter here to lamda? Taking it out compiles, but *db*
> gets assigned a list of NILs.

The above ROW is the second expression in the body of the lambda. The
body can be one or more expressions, because it is an "implicit progn".

  (lambda (row)
    (when (funcall ...) ...)
    row)

If you take it out, then the return value of the function is whatever
comes out of the WHEN form.


The MAPCAR collects a list of the return values, which is assignd back
to  *DB*.

I don't see why MAPCAR is used here at all, or why *DB* is assigned to.
The value of ROW never changes in the body of the lambda, so  the value
in *DB* is duplicated onto itself for nothing.

The (SETF (GETF ...)) expressions don't change the value of ROW. they
assume that ROW holds a symbol, and they  manipulate the property list
of that symbol.

Moreover, I'm puzzled why the author would use WHEN, but then turn
around and use IF with no alternate clause right in the middle of it.

You could rewrite this logic as something like:

  (defun update-properties (selector-fn &key title artist rating
                            (ripped nil ripped-p))
    (loop for row-symbol in *db*
          when (funcall selector-fn row-symbol)
          do
            (progn
              (when title (setf (getf row-symbol :title) title))
              (when artist (setf (getf row-symbol :artist) artist))
              (when rating (setf (getf row-symbol :rating) rating))
              (when ripped-p (setf (getf row-symbol :ripped)
                                   ripped)))))

are we missing anything?

If you want to do this with a mapping function rather than LOOP, then
MAPC is a better choice. It's like MAPCAR that has been hacked down not
to collect the return values of the function into a list and
consequently to return nothing. Since nothing is returned, the function
ought to have some kind of side effect on the state of the program.

So instead of switching to LOOP, you can take out the (SETF *DB* ...)
assignment, change MAPCAR to MAPC, and get rid of the ROW return value
at the end of the lambda.

> So, addl version works the way I thought it would.  Adding a 3rd param
> "num" to lambda in addl still compiles(!) but now addl does not modify
> *l* (oddly, roughly opposite from the behavior of update).

Because now your lambda function is the same as #'IDENTITY.  The
results of the (+ X NUM) expression are thrown away, and NUM is
returned.

  (mapcar #'identity list)

does the same thing as COPY-LIST, and

  (lambda (num) (+ num 1) num)

does the same thing as IDENTITY.
From: Juho Snellman
Subject: Re: PCL: question about lambda usage
Date: 
Message-ID: <slrndvskuf.bgu.jsnell@sbz-30.cs.Helsinki.FI>
Kaz Kylheku <········@gmail.com> wrote:
> The (SETF (GETF ...)) expressions don't change the value of ROW. they
> assume that ROW holds a symbol, and they  manipulate the property list
> of that symbol.

(SETF GETF) works on plists, not symbols. Also, (SETF (GETF ROW ...)
...) can change the value of ROW (i.e. assign a different value to
it).

> You could rewrite this logic as something like:
> 
>   (defun update-properties (selector-fn &key title artist rating
>                             (ripped nil ripped-p))
>     (loop for row-symbol in *db*
>           when (funcall selector-fn row-symbol)
>           do
>             (progn
>               (when title (setf (getf row-symbol :title) title))
>               (when artist (setf (getf row-symbol :artist) artist))
>               (when rating (setf (getf row-symbol :rating) rating))
>               (when ripped-p (setf (getf row-symbol :ripped)
>                                    ripped)))))
> 
> are we missing anything?

Yes. This is completely wrong, since SETF of GETF is not guaranteed to
modify the plist destructively. As for style... Why are you using an
explicit PROGN instead of taking advantage of the implicit PROGN of
the DO clause?

-- 
Juho Snellman
From: Kaz Kylheku
Subject: Re: PCL: question about lambda usage
Date: 
Message-ID: <1140757880.301406.149740@t39g2000cwt.googlegroups.com>
Juho Snellman wrote:
> Kaz Kylheku <········@gmail.com> wrote:
> > The (SETF (GETF ...)) expressions don't change the value of ROW. they
> > assume that ROW holds a symbol, and they  manipulate the property list
> > of that symbol.
>
> (SETF GETF) works on plists, not symbols. Also, (SETF (GETF ROW ...)
> ...) can change the value of ROW (i.e. assign a different value to
> it

Shit!

All right, that's the /last/ time I mix up GET and GETF, I swear.
From: Jyri J. Virkki
Subject: Re: PCL: question about lambda usage
Date: 
Message-ID: <11vt2mom45utj82@corp.supernews.com>
In article <························@p10g2000cwp.googlegroups.com>,
Kaz Kylheku <········@gmail.com> wrote:
>
>The above ROW is the second expression in the body of the lambda. The
>body can be one or more expressions, because it is an "implicit progn".
>
>  (lambda (row)
>    (when (funcall ...) ...)
>    row)

Thanks. All prior examples had shown a single expression so I did not
realize it was legal to have multiple ones in the lambda body.  With
that clarification, all the cases make sense now.

-- 
Jyri J. Virkki - Santa Cruz, CA
From: Peter Seibel
Subject: Re: PCL: question about lambda usage
Date: 
Message-ID: <m2y800r7wy.fsf@gigamonkeys.com>
"Kaz Kylheku" <········@gmail.com> writes:

> Jyri J. Virkki wrote:
>> In PCL chapter 3,
>>
>> (defun update (selector-fn &key title artist rating (ripped nil ripped-p))
>>   (setf *db*
>>         (mapcar
>>          #'(lambda (row)
>>              (when (funcall selector-fn row)
>>                (if title    (setf (getf row :title) title))
>>                (if artist   (setf (getf row :artist) artist))
>>                (if rating   (setf (getf row :rating) rating))
>>                (if ripped-p (setf (getf row :ripped) ripped)))
>>              row) *db*)))
>>              ^^^
>> Why the row parameter here to lamda? Taking it out compiles, but *db*
>> gets assigned a list of NILs.

> I don't see why MAPCAR is used here at all, or why *DB* is assigned to.
> The value of ROW never changes in the body of the lambda, so  the value
> in *DB* is duplicated onto itself for nothing.
>
> The (SETF (GETF ...)) expressions don't change the value of ROW. they
> assume that ROW holds a symbol, and they  manipulate the property list
> of that symbol.

As Juho pointed out, you're mixing up GET and GETF. However there's
another subtlety here, which is that GETF probably is probably
modifying the actual plists referred to by ROW since they already have
the specified keys in them. However that behavior is not specified and
thus can't be relied upon. So in reality this code would probably work
with MAPC (or DOLIST or LOOP) and no assignment to *DB* but it would
be incorrect.

> Moreover, I'm puzzled why the author would use WHEN, but then turn
> around and use IF with no alternate clause right in the middle of it.

Dunno about that. I tend to use WHEN, UNLESS, and IF in a more
"natural language" way. I.e. more like transcribing how I'd describe
the algorithm in English, "loop through the rows in db; when the
selector matches do the following: if title is specified set the title
..., etc.

-Peter

-- 
Peter Seibel           * ·····@gigamonkeys.com
Gigamonkeys Consulting * http://www.gigamonkeys.com/
Practical Common Lisp  * http://www.gigamonkeys.com/book/
From: David Sletten
Subject: Re: PCL: question about lambda usage
Date: 
Message-ID: <EBtLf.22$xS5.21@tornado.socal.rr.com>
Jyri J. Virkki wrote:
> In PCL chapter 3,
> 
> (defun update (selector-fn &key title artist rating (ripped nil ripped-p))
>   (setf *db*
>         (mapcar
>          #'(lambda (row)
>              (when (funcall selector-fn row)
>                (if title    (setf (getf row :title) title))
>                (if artist   (setf (getf row :artist) artist))
>                (if rating   (setf (getf row :rating) rating))
>                (if ripped-p (setf (getf row :ripped) ripped)))
>              row) *db*)))
>              ^^^
> Why the row parameter here to lamda? Taking it out compiles, but *db*
> gets assigned a list of NILs.
> 
> I tried a simpler example to experiment:
> 
> (defvar *l* '(1 2 3 4 5))
> (defun addl (x)
>   (setf *l* (mapcar #'(lambda (num) (+ x num)) *l*)))
> 
> CL-USER> *l*
> (1 2 3 4 5)
> CL-USER> (addl 3)
> (4 5 6 7 8)
> 
> So, addl version works the way I thought it would.  Adding a 3rd param
> "num" to lambda in addl still compiles(!) but now addl does not modify
> *l* (oddly, roughly opposite from the behavior of update).
> 
> Browsing ahead in the book a bit (Ch.5) it reiterates the form of lambda as:
> (lambda (parameters) body)
> 
> So I'm confused. What's the 3rd param to lambda and why is it needed
> in the update function example?
> 
> Thanks..
> 

MAPCAR takes a function and 1 or more lists and creates a list of values 
by applying the function to each input element(s) in sequence. It maps 
each element of the input list(s) (domain) to its image under the 
function. If you use a predefined function, then you don't need to 
bother with LAMBDA at all:
(mapcar #'1+ '(1 2 3 4 5)) => (2 3 4 5 6)

LAMBDA simply lets you describe a function without naming it:
(mapcar #'(lambda (x) (+ x 1)) '(1 2 3 4 5))

This produces the same result because we are describing the same 
function that is defined with the name 1+, a function that takes a 
single argument and returns the number one greater than it.

You can play around with LAMBDA to understand how it works:
(1+ 5)
((lambda (x) (+ x 1)) 5)

(+ 2 9)
((lambda (x y) (+ x y)) 2 9)

You must observe that the value returned by the function which is 
MAPCAR's first argument becomes the value that is placed into the 
results list. Remember that the value returned by a function is the 
value of the last form evaluated in that function. The following 
produces a different result than the example above:
((lambda (x) (+ x 1) nil) 5) => NIL

So that if we tried this we'd get something different too (probably not 
what we wanted):
(mapcar #'(lambda (x) (+ x 1) nil) '(1 2 3 4 5)) => (NIL NIL NIL NIL NIL)

In the PCL example you are working on, the MAPCAR maps a list of rows 
(*DB*) to a new list of rows, which becomes the new value of *DB*. Each 
input row is modified as necessary and then the modified row becomes the 
output of the LAMBDA function. If you remove the final (second) form of 
the LAMBDA function, namely ROW, then the final form of the function is 
the WHEN expression. This would then be the value returned by the 
function. However, WHEN always returns NIL, which is what you see 
accumulated in your result.

Aloha,
David Sletten