From: Thaddeus L Olczyk
Subject: mapcar-if
Date: 
Message-ID: <3c5c2e63.522412171@nntp.interaccess.com>
Is there a function, call it mapcar-if,
which acts like mapcar with one tiny exception?
If the return value of the lambda expression is nil,
then it does not get inserted into the list returned by mapcar.

From: Thomas F. Burdick
Subject: Re: mapcar-if
Date: 
Message-ID: <xcvadurvhgi.fsf@famine.OCF.Berkeley.EDU>
······@interaccess.com (Thaddeus L Olczyk) writes:

> Is there a function, call it mapcar-if,
> which acts like mapcar with one tiny exception?
> If the return value of the lambda expression is nil,
> then it does not get inserted into the list returned by mapcar.

(mapcan #'(lambda (x) (when (oddp x) (list x)))
        '(1 2 3 4 5 6 7 8 9 10))
=> (1 3 5 7 9)

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Carl Shapiro
Subject: Re: mapcar-if
Date: 
Message-ID: <ouypu3nk9nv.fsf@panix3.panix.com>
······@interaccess.com (Thaddeus L Olczyk) writes:

> Is there a function, call it mapcar-if,
> which acts like mapcar with one tiny exception?
> If the return value of the lambda expression is nil,
> then it does not get inserted into the list returned by mapcar.

You can do what you want with MAPCAN.

(mapcan #'(lambda (x) (and (evenp x) (list x))) '(1 2 3 4 5))
=> (2 4) 
From: Tim Bradshaw
Subject: Re: mapcar-if
Date: 
Message-ID: <ey33d0jiv8y.fsf@cley.com>
* Thaddeus L Olczyk wrote:
> Is there a function, call it mapcar-if,
> which acts like mapcar with one tiny exception?
> If the return value of the lambda expression is nil,
> then it does not get inserted into the list returned by mapcar.

MAPCAN is almost this but somewhat more general.  The function should
return a (freshly consed) list.  The lists returned are spliced
together with NCONC to create the returned list.  So if you return NIL
the result is effectively ignored, if you return a list of one element
then that's like MAPCAR, but you can also return more than one thing
if you want to. Make sure you freshly cons the returned list or very
odd things can happen...

--tim
From: Thaddeus L Olczyk
Subject: Re: mapcar-if
Date: 
Message-ID: <3c5d325a.523428000@nntp.interaccess.com>
On Sat, 02 Feb 2002 18:29:31 GMT, ······@interaccess.com (Thaddeus L
Olczyk) wrote:

>Is there a function, call it mapcar-if,
>which acts like mapcar with one tiny exception?
>If the return value of the lambda expression is nil,
>then it does not get inserted into the list returned by mapcar.
Let me add that I can com up with some solutions on my own,
but I believe ti can be done more efficiently than my homebrew.
From: Kaz Kylheku
Subject: Re: mapcar-if
Date: 
Message-ID: <YVX68.11570$2x2.622541@news3.calgary.shaw.ca>
In article <··················@nntp.interaccess.com>, Thaddeus L Olczyk wrote:
>Is there a function, call it mapcar-if,
>which acts like mapcar with one tiny exception?
>If the return value of the lambda expression is nil,
>then it does not get inserted into the list returned by mapcar.

Yes, mapcan. RTFM.

Under mapcan, the function is expected to return a list. 
The returned lists are destructively catenated into one big
list that is returned. Any NIL outputs from the function disappear
in the catenation.
From: Steven M. Haflich
Subject: Re: mapcar-if
Date: 
Message-ID: <3C5D9C11.C16B9319@pacbell.net>
Kaz Kylheku wrote:
> 
> In article <··················@nntp.interaccess.com>, Thaddeus L Olczyk wrote:
> >Is there a function, call it mapcar-if,
> >which acts like mapcar with one tiny exception?
> >If the return value of the lambda expression is nil,
> >then it does not get inserted into the list returned by mapcar.
> 
> Yes, mapcan. RTFM.
> 
> Under mapcan, the function is expected to return a list.
> The returned lists are destructively catenated into one big
> list that is returned. Any NIL outputs from the function disappear
> in the catenation.

This suggestion to use mapcan doesn't quite satisfy the original
request, since (as you observe) it requires rewriting the function
argument to return a list.  It is an important property of mapcar
and friends that the fuctional argument is opaque.  While these
functions can sometimes be optimized by the compiler if the mapped
function is visible, the functions work even if not.  So rewriting
the function is not an acceptable option.

Here are two solutions.  The first simply relies on mapcar and uses
delete to remove any nils from the list of results:

(defun mapcar-if (function &rest lists)
  (delete 'nil (apply #'mapcar function lists)))

The second opencodes everything.  Consequently, it needs to be
programmed much more carefully in order to do the all argument
checking etc. that the ANS requires of mapcar.

(defun mapcar-if (function list &rest more-lists
		  &aux (lists (cons list more-lists)))
  (loop as e = (apply function
		      (loop for x on lists
			  when (null (car x))
			  do (return-from mapcar-if result)
			  collect (pop (car x))))
      when e collect e into result))
From: Thomas F. Burdick
Subject: Re: mapcar-if
Date: 
Message-ID: <xcvn0yq6xa7.fsf@conquest.OCF.Berkeley.EDU>
"Steven M. Haflich" <·······@pacbell.net> writes:

> Kaz Kylheku wrote:
> > 
> > In article <··················@nntp.interaccess.com>, Thaddeus L Olczyk wrote:
> > >Is there a function, call it mapcar-if,
> > >which acts like mapcar with one tiny exception?
> > >If the return value of the lambda expression is nil,
> > >then it does not get inserted into the list returned by mapcar.
> > 
> > Yes, mapcan. RTFM.
> > 
> > Under mapcan, the function is expected to return a list.
> > The returned lists are destructively catenated into one big
> > list that is returned. Any NIL outputs from the function disappear
> > in the catenation.
> 

> This suggestion to use mapcan doesn't quite satisfy the original
> request, since (as you observe) it requires rewriting the function
> argument to return a list.

That's true.  I'm one of the people who suggested mapcan, figuring
that the OP could do one of two things with that information: (a)
rewrite his code to use the mapcan idiom, or (b) very easily write
mapcar-if, using mapcan.  I was sort of hoping he'd go for the second,
and learn something along the way.

> Here are two solutions.  The first simply relies on mapcar and uses
> delete to remove any nils from the list of results:
> 
> (defun mapcar-if (function &rest lists)
>   (delete 'nil (apply #'mapcar function lists)))

Or, a third way:

  (defun mapcar-if (function &rest lists)
    (let ((fn #'(lambda (&rest args)
                  (let ((result (apply function args)))
                    (when result (list result))))))
      (declare (dynamic-extent fn))
      (apply #'mapcan fn lists)))

> The second opencodes everything.  Consequently, it needs to be
> programmed much more carefully in order to do the all argument
> checking etc. that the ANS requires of mapcar.
> 
> (defun mapcar-if (function list &rest more-lists
> 		  &aux (lists (cons list more-lists)))
>   (loop as e = (apply function
> 		      (loop for x on lists
> 			  when (null (car x))
> 			  do (return-from mapcar-if result)
> 			  collect (pop (car x))))
>       when e collect e into result))

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Kaz Kylheku
Subject: Re: mapcar-if
Date: 
Message-ID: <Yph78.18229$2x2.938774@news3.calgary.shaw.ca>
In article <·················@pacbell.net>, Steven M. Haflich wrote:
>
>Kaz Kylheku wrote:
>> 
>> In article <··················@nntp.interaccess.com>, Thaddeus L Olczyk wrote:
>> >Is there a function, call it mapcar-if,
>> >which acts like mapcar with one tiny exception?
>> >If the return value of the lambda expression is nil,
>> >then it does not get inserted into the list returned by mapcar.
>> 
>> Yes, mapcan. RTFM.
>> 
>> Under mapcan, the function is expected to return a list.
>> The returned lists are destructively catenated into one big
>> list that is returned. Any NIL outputs from the function disappear
>> in the catenation.
>
>This suggestion to use mapcan doesn't quite satisfy the original
>request, since (as you observe) it requires rewriting the function
>argument to return a list.

A rewrite is not absolutely necessary, which is why I didn't observe it
that way.  Thanks to closures and higher order functions, we can create
an adapter object on the fly to change a function's apparent
interface:

(defun non-nil-value-to-list (f) 
  #'(lambda (x) 
      (let ((y (funcall f x))) 
        (if y (list y)))))

(mapcan (non-nil-value-to-list #'whatever-func) whatever-list)

This is not always an issue. Often one can tailor the function to
mapcan. This is always the case when it's a closure formed right at the
call to mapcan.
From: Steven M. Haflich
Subject: Re: mapcar-if
Date: 
Message-ID: <3C5E3811.3FB0FC72@pacbell.net>
Kaz Kylheku wrote:
> A rewrite is not absolutely necessary, which is why I didn't observe it
> that way.  Thanks to closures and higher order functions, we can create
> an adapter object on the fly to change a function's apparent
> interface:
> 
> (defun non-nil-value-to-list (f)
>   #'(lambda (x)
>       (let ((y (funcall f x)))
>         (if y (list y)))))
> 
> (mapcan (non-nil-value-to-list #'whatever-func) whatever-list)
> 
> This is not always an issue. Often one can tailor the function to
> mapcan. This is always the case when it's a closure formed right at the
> call to mapcan.

With all respect:

I find your argument hard to accept.  The original request was for a
_function_ that was like mapcar except blah blah but the alternative
you now suggest is not a function -- rather, it is an expression.
I'm sure you will now have no problem restating this line of code into
a real function that incorporates your higher-order function (or see
Thomas Burdick's revision) but you have for the second time missed
implementing the requested specification.
From: Barry Margolin
Subject: Re: mapcar-if
Date: 
Message-ID: <_Jy78.6$Xh.232127@burlma1-snr2>
In article <·················@pacbell.net>,
Steven M. Haflich <···@alum.mit.edu> wrote:
>
>Kaz Kylheku wrote:
>> A rewrite is not absolutely necessary, which is why I didn't observe it
>> that way.  Thanks to closures and higher order functions, we can create
>> an adapter object on the fly to change a function's apparent
>> interface:
>> 
>> (defun non-nil-value-to-list (f)
>>   #'(lambda (x)
>>       (let ((y (funcall f x)))
>>         (if y (list y)))))
>> 
>> (mapcan (non-nil-value-to-list #'whatever-func) whatever-list)
>> 
>> This is not always an issue. Often one can tailor the function to
>> mapcan. This is always the case when it's a closure formed right at the
>> call to mapcan.
>
>With all respect:
>
>I find your argument hard to accept.  The original request was for a
>_function_ that was like mapcar except blah blah but the alternative
>you now suggest is not a function -- rather, it is an expression.
>I'm sure you will now have no problem restating this line of code into
>a real function that incorporates your higher-order function (or see
>Thomas Burdick's revision) but you have for the second time missed
>implementing the requested specification.

It's such a trivial restatement that he probably didn't realize it was
necessary to write it out:

(defun mapcar-if (f &rest args)
  (apply #'mapcan (non-nil-value-to-list f) args))

-- 
Barry Margolin, ······@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Wolfhard Buß
Subject: Re: mapcar-if
Date: 
Message-ID: <m31yg0n5lv.fsf@buss-14250.user.cis.dfn.de>
"Steven M. Haflich" <·······@pacbell.net> writes:

> The second opencodes everything.  Consequently, it needs to be
> programmed much more carefully in order to do the all argument
> checking etc. that the ANS requires of mapcar.
> 
> (defun mapcar-if (function list &rest more-lists
> 		  &aux (lists (cons list more-lists)))
>   (loop as e = (apply function
> 		      (loop for x on lists
> 			  when (null (car x))
> 			  do (return-from mapcar-if result)
> 			  collect (pop (car x))))
>       when e collect e into result))

One could rewrite your mapcar-if using the well known zip :

 (defun mapcar-if (function list &rest more-lists
                   &aux (lists (cons list more-lists)))
   (let ((result '()))
     (dolist (items (zip lists) (nreverse result))
       (let ((value (apply function items)))
         (when value (push value result))))))

-- 
"Das Auto hat keine Zukunft. Ich setze aufs Pferd."  Wilhelm II. (1859-1941)
From: Matthieu Villeneuve
Subject: Re: mapcar-if
Date: 
Message-ID: <3C5F305D.981BB114@tumbleweed.com>
Wolfhard Bu� wrote:
> 
> "Steven M. Haflich" <·······@pacbell.net> writes:
> 
> > The second opencodes everything.  Consequently, it needs to be
> > programmed much more carefully in order to do the all argument
> > checking etc. that the ANS requires of mapcar.
> >
> > (defun mapcar-if (function list &rest more-lists
> >                 &aux (lists (cons list more-lists)))
> >   (loop as e = (apply function
> >                     (loop for x on lists
> >                         when (null (car x))
> >                         do (return-from mapcar-if result)
> >                         collect (pop (car x))))
> >       when e collect e into result))
> 
> One could rewrite your mapcar-if using the well known zip :
> 
>  (defun mapcar-if (function list &rest more-lists
>                    &aux (lists (cons list more-lists)))
>    (let ((result '()))
>      (dolist (items (zip lists) (nreverse result))
>        (let ((value (apply function items)))
>          (when value (push value result))))))
> 
> --
> "Das Auto hat keine Zukunft. Ich setze aufs Pferd."  Wilhelm II. (1859-1941)

I don't know that "well known zip", could you tell a bit more about it?
:)
Thanks,

--
Matthieu Villeneuve
http://www.matthieu-villeneuve.net
From: Wolfhard Buß
Subject: Re: mapcar-if
Date: 
Message-ID: <m37kpsw573.fsf@buss-14250.user.cis.dfn.de>
Matthieu Villeneuve <···················@tumbleweed.com> writes:

> I don't know that "well known zip", could you tell a bit more about it?

Sure. zip is something like
 (lambda (lists) (apply #'mapcar #'list lists))

Look at
 http://groups.google.com/groups?hl=en&····················@world.std.com
From: Marco Antoniotti
Subject: Re: mapcar-if
Date: 
Message-ID: <y6cy9i8sxqk.fsf@octagon.mrl.nyu.edu>
······@interaccess.com (Thaddeus L Olczyk) writes:

> Is there a function, call it mapcar-if,
> which acts like mapcar with one tiny exception?
> If the return value of the lambda expression is nil,
> then it does not get inserted into the list returned by mapcar.

(defun mapcar-if (pred list)
   (mapcan #'(lambda (x) (when (funcall pred x) (list x))) list))

Cheers



-- 
Marco Antoniotti ========================================================
NYU Courant Bioinformatics Group        tel. +1 - 212 - 998 3488
719 Broadway 12th Floor                 fax  +1 - 212 - 995 4122
New York, NY 10003, USA                 http://bioinformatics.cat.nyu.edu
                    "Hello New York! We'll do what we can!"
                           Bill Murray in `Ghostbusters'.