Re: I still don't get MAPCAN



"jmckitrick" <j_mckitrick@xxxxxxxxx> writes:

Bill Atkins wrote:
Do those lists look the same to you? They look like pretty different
results to me.

The CLHS, believe it or not, is very helpful here:

"mapcan and mapcon are like mapcar and maplist respectively, except
that the results of applying function are combined into a list by the
use of nconc rather than list."

See the part about NCONC'ing the return values together? Funny what
you can find in the spec.

It's not the literal effect I don't understand. It's if MAPCAN is
supposed to be the corresponding 'destructive' version of MAPCAR,
unless these two are not complimentary in that way.

Example:
CL-USER> (nconc '(1 2 3) '(4 5 6))
(1 2 3 4 5 6)
CL-USER> (append '(1 2 3) '(4 5 6))
(1 2 3 4 5 6)
CL-USER>

These produce the same result, but with different side effects. I
thought MAPCAR and MAPCAN had the same relationship, but that cannot
be, if the results are different.

Apparently, though, MAPCAN is not just a 'destructive' version of
MAPCAR. That's what I was trying to clarify.

Just ask it! ;-)


(setf *print-circle* t)

(let ((as (list 'one 'two 'three))
(bs (list 'un 'deux 'trois))
(cared-objects '())
(caned-objects '()))
(let ((mapcar-result (mapcar (lambda (a b)
(let ((object (list a b)))
(push object cared-objects)
object))
as bs))
(mapcan-result (mapcan (lambda (a b)
(let ((object (list a b)))
(push object caned-objects)
object))
as bs)))
(setf cared-objects (nreverse cared-objects)
caned-objects (nreverse caned-objects))
(list
`(as ,as)
`(bs ,bs)
`(cared-objects ,cared-objects)
`(mapcar-result ,mapcar-result)
`(caned-objects ,caned-objects)
`(mapcan-result ,mapcan-result))))

-->

((AS (ONE TWO THREE))
(BS (UN DEUX TROIS))
(CARED-OBJECTS (#1=(ONE UN) #2=(TWO DEUX) #3=(THREE TROIS)))
(MAPCAR-RESULT (#1# #2# #3#))
(CANED-OBJECTS (#4=(ONE UN . #5=(TWO DEUX . #6=(THREE TROIS))) #5# #6#))
(MAPCAN-RESULT #4#))


With *print-circle*, any non-symbol object that appears several times
in the s-expr is first identified with #N= and then referenced with
#N#. You can then see where the same CONS cell is referenced twice
(or more). (interned symbols printed identically are identical by
definition, so there's no need to number them).



In the mapcar part, you see that each of the objects returned by the
lambda #1#, #2# and #3# are collected in a new list created and
returned by mapcar (no other references to the CONS cells of this list
exist anywhere else).

In the mapcan part, on the contrary you see that the CONS cells of the
list returned by mapcan are actually the CONS cells returned by the
lambda.

The first object put on the caned-objects list was #4=(ONE . (UN . NIL))
The second object put on the caned-objects list was #5=(TWO . (DEUX . NIL))

When this second object was returned by the lambda, MAPCAN modified
the CDR of the LAST cell of the first object #4#, doing something like:

(setf (cdr (last #4#)) #5#)

Hence, now you see that #4# include #5#:

#4=(ONE . (UN . #5=(TWO . (DEUX . NIL))))

The third object put on the caned-object list was #6#, and when
returned, has been assigned by MAPCAN to the CDR of the new LAST
cell, of the object #5#:

(setf (cdr (last #4#)) #6#)

Hence, now you see that #4# include #5#:

#4=(ONE . (UN . #5=(TWO . (DEUX . #6=(THREE . (TROIS . NIL))))))
or:
#4=(ONE UN . #5=(TWO DEUX . #6=(THREE TROIS)))

as the printer puts it. Finally, this is this list that is returned
by MAPCAN. MAPCAN didn't CONS any list. It modified the lists it got
from the lambda, and concatenated them into one big list.



Exercise: draw a diagram of boxes and arrows representing the whole result:
((AS (ONE TWO THREE))
(BS (UN DEUX TROIS))
(CARED-OBJECTS (#1=(ONE UN) #2=(TWO DEUX) #3=(THREE TROIS)))
(MAPCAR-RESULT (#1# #2# #3#))
(CANED-OBJECTS (#4=(ONE UN . #5=(TWO DEUX . #6=(THREE TROIS))) #5# #6#))
(MAPCAN-RESULT #4#))

--
__Pascal Bourguignon__ http://www.informatimago.com/

"You can tell the Lisp programmers. They have pockets full of punch
cards with close parentheses on them." --> http://tinyurl.com/8ubpf
.



Relevant Pages

  • Re: I still dont get MAPCAN
    ... "mapcan and mapcon are like mapcar and maplist respectively, ... supposed to be the corresponding 'destructive' version of MAPCAR, ... that the function returns a list, and it catenates the lists together. ... the lists returned by the function as if by APPEND rather than NCONC. ...
    (comp.lang.lisp)
  • Re: mapcar-if
    ... If the return value of the lambda expression is nil, ... then it does not get inserted into the list returned by mapcar. ... Under mapcan, the function is expected to return a list. ... The returned lists are destructively catenated into one big ...
    (comp.lang.lisp)
  • Re: novice: mapcan use?
    ... interesting because it can be used as a filter, ... However, mapcan is somewhat old-fashioned. ... filtering function has to return the elements wrapped in lists. ... Watching you program is like watching a hippopotamus dance. ...
    (comp.lang.lisp)
  • Re: novice: mapcan use?
    ... interesting because it can be used as a filter, ... However, mapcan is somewhat old-fashioned. ... filtering function has to return the elements wrapped in lists. ... Watching you program is like watching a hippopotamus dance. ...
    (comp.lang.lisp)
  • Re: Opposite of ~^ FORMAT Directive
    ... use some sort of circular list thing, ... (mapcar #'list;this mapcar will stop as long as x is not circular! ... MAPCAR function &rest lists => result list ... but it cannot change the language. ...
    (comp.lang.lisp)