From: Lowell
Subject: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <bg2mi0$odi$1@mughi.cs.ubc.ca>
I was wondering if there was a more concise way of doing something using 
built-in functions: Given a list lst and a function fn, I want to 
construct a list which contains the results of applying F to memmbers of 
L and only keeps the ones which return a true value. It is much simpler 
in code:

(member-if identity (map #'fn lst))

I would expect that there would be soemthing built into CL which would 
allow me to do this without the 'identity'. I know I could easily write 
my own function, I was just wondering if there was something built in.

Lowell

From: Lowell
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <bg2mj8$odh$1@mughi.cs.ubc.ca>
...of course, I meant fn and lst

Lowell wrote:

> ... F to memmbers of L ...
From: Pascal Costanza
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <bg2nth$hce$1@f1node01.rhrz.uni-bonn.de>
Lowell wrote:
> I was wondering if there was a more concise way of doing something using 
> built-in functions: Given a list lst and a function fn, I want to 
> construct a list which contains the results of applying F to memmbers of 
> L and only keeps the ones which return a true value. It is much simpler 
> in code:
> 
> (member-if identity (map #'fn lst))

I know about three ways to do that:

(mapcan (lambda (elem)
            (when (fn elem)
               (list (fn elem))))
         list)

(loop for elem in list
       when (fn elem) collect (fn elem))

or check out the excellent collecting macros at 
http://www.tfeb.org/lisp/hax.html#COLLECTING

You might want to make sure that fn is only applied once to each 
element. You can do that with when-let in Lispworks. Or use the 
following idiom:

(let ((var (fn elem)))
   (when var (list var)))

Or the following macro:

(defmacro whenlet ((var exp) &body body)
   `(let ((,var ,exp))
      (when ,var ,@body)))


Pascal

-- 
Pascal Costanza               University of Bonn
···············@web.de        Institute of Computer Science III
http://www.pascalcostanza.de  R�merstr. 164, D-53117 Bonn (Germany)
From: Lars Brinkhoff
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <85brvfauhk.fsf@junk.nocrew.org>
Pascal Costanza <········@web.de> writes:
> (loop for elem in list
>        when (fn elem) collect (fn elem))
> 
> You might want to make sure that fn is only applied once to each
> element. You can do that with when-let in Lispworks. Or ...

Or use "collect it".

-- 
Lars Brinkhoff,         Services for Unix, Linux, GCC, PDP-10, HTTP
Brinkhoff Consulting    http://www.brinkhoff.se/
From: Pascal Costanza
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <bg2qq0$lbg$1@f1node01.rhrz.uni-bonn.de>
Lars Brinkhoff wrote:
> Pascal Costanza <········@web.de> writes:
> 
>>(loop for elem in list
>>       when (fn elem) collect (fn elem))
>>
>>You might want to make sure that fn is only applied once to each
>>element. You can do that with when-let in Lispworks. Or ...
> 
> 
> Or use "collect it".

You mean

(loop for elem in list
       when (fn elem) collect it)

Cool - didn't know about this one!


Pascal


-- 
Pascal Costanza               University of Bonn
···············@web.de        Institute of Computer Science III
http://www.pascalcostanza.de  R�merstr. 164, D-53117 Bonn (Germany)
From: Chris Perkins
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <6cb6c81f.0307281233.7c685b16@posting.google.com>
Pascal Costanza <········@web.de> wrote in message news:<bg2qq0
> 
> You mean
> 
> (loop for elem in list
>        when (fn elem) collect it)
> 
> Cool - didn't know about this one!
> 

I love the loop macro, though I know many Lispniks are not fond of it.

What I like is that most languages only express the most minimal
uninteresting things about the loop and you must carefully examine the
body of the loop to see what is transpiring.  So every loop, no matter
what it is doing, looks like every other loop:
for(i=0; i<=n; i++)

But in Lisp, the rich loop vocabulary clearly expresses what's going
on. Are we counting, collecting, 'tupling'? Are there side-effects
(:do) or is it clean?  All of this, and more, is immediately obvious
when the loop macro is used correctly.

Chris Perkins
From: Coby Beck
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <bg2p1v$2rng$1@otis.netspace.net.au>
"Pascal Costanza" <········@web.de> wrote in message
·················@f1node01.rhrz.uni-bonn.de...
> Lowell wrote:
> > I was wondering if there was a more concise way of doing something using
> > built-in functions: Given a list lst and a function fn, I want to
> > construct a list which contains the results of applying F to memmbers of
> > L and only keeps the ones which return a true value. It is much simpler
> > in code:
> >
> > (member-if identity (map #'fn lst))
>
> I know about three ways to do that:
>
> (mapcan (lambda (elem)
>             (when (fn elem)
>                (list (fn elem))))
>          list)
>
> (loop for elem in list
>        when (fn elem) collect (fn elem))
>
> or check out the excellent collecting macros at
> http://www.tfeb.org/lisp/hax.html#COLLECTING
>
> You might want to make sure that fn is only applied once to each
> element. You can do that with when-let in Lispworks. Or use the
> following idiom:

You could also do:
(loop for elem in (mapcar #'fn list)
      when elem collect elem)

I always used to do:

(remove nil (mapcar #'fn list))

in that situation...

-- 
Coby Beck
(remove #\Space "coby 101 @ big pond . com")
From: Kenny Tilton
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <3F252C30.5080102@nyc.rr.com>
Coby Beck wrote:
> (remove nil (mapcar #'fn list))

not delete?

-- 

  kenny tilton
  clinisys, inc
  http://www.tilton-technology.com/
  ---------------------------------------------------------------
"Everything is a cell." -- Alan Kay
From: Coby Beck
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <bg4s7f$1llb$1@otis.netspace.net.au>
"Kenny Tilton" <·······@nyc.rr.com> wrote in message
·····················@nyc.rr.com...
>
>
> Coby Beck wrote:
> > (remove nil (mapcar #'fn list))
>
> not delete?

I guess that falls under the category of harmless yet premature
optimization...I just never think of destructive functions unless I have a
specific reason to.

I agree it is a better choice, all else being equal.

-- 
Coby Beck
(remove #\Space "coby 101 @ big pond . com")
From: Kenny Tilton
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <3F268F25.9010900@nyc.rr.com>
Coby Beck wrote:
> "Kenny Tilton" <·······@nyc.rr.com> wrote in message
> ·····················@nyc.rr.com...
> 
>>
>>Coby Beck wrote:
>>
>>>(remove nil (mapcar #'fn list))
>>
>>not delete?
> 
> 
> I guess that falls under the category of harmless yet premature
> optimization...I just never think of destructive functions unless I have a
> specific reason to.

Ah, invoking the hobgoblin of premature optimization! <g> I'll rebut 
that last. But first, this "I just never think of destructive functions 
unless..." is exactly the disease I do not want to encourage in newbies 
by saying "don't use destructive functions for the first N months of 
Lisp" or some such.

First of all, there is no getting around the need to understand the 
structure of lists. So let's go ahead and explain it! In which case we 
can also explain how destructive operations can mess things up. A 
five-line example could make the case. Speaking of which, someone just 
found a bug in Cells--who knew remf was destructive!? (<duhhh!>, now 
that I contemplate the f in remf.) Anyway...

Second, (remove nil (mapcar ...)) falls into the category of something 
where it is very easy to feel safe in using a destructive operation, 
since you can see the brand spanking new structure being formed and 
abandoned (no reference kept) right in that very line of code.

As for the preemy opt hobgoblin, it does not take any longer to type 
delete than remove, you just gotta know that mapcar generates new 
structure. there is a difference between optimizations which involve 
bizarre contortions which work only for the implementation as it stands 
(hence won't survive refactoring, hence should not be done prematurely) 
and simply writing efficient code.

While discussing some issue with the lisp-nyc RoboCup team I said, 
"Avoiding premature optimization does not mean deliberately writing slow 
code." The context was me justifying a little heavy-lifting I could have 
avoided (but experience told me the code in question had to be fast), 
but it also applies to being too lazy to choose between delete and 
remove, or nconc and append.

-- 

  kenny tilton
  clinisys, inc
  http://www.tilton-technology.com/
  ---------------------------------------------------------------
"Everything is a cell." -- Alan Kay
From: Coby Beck
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <bg7a5f$q6k$1@otis.netspace.net.au>
"Kenny Tilton" <·······@nyc.rr.com> wrote in message
·····················@nyc.rr.com...
> Coby Beck wrote:
> > "Kenny Tilton" <·······@nyc.rr.com> wrote in message
> > ·····················@nyc.rr.com...
> >
> >>
> >>Coby Beck wrote:
> >>
> >>>(remove nil (mapcar #'fn list))
> >>
> >>not delete?
> >
> >
> > I guess that falls under the category of harmless yet premature
> > optimization...I just never think of destructive functions unless I have
a
> > specific reason to.
>
> Ah, invoking the hobgoblin of premature optimization! <g> I'll rebut
> that last. But first, this "I just never think of destructive functions
> unless..." is exactly the disease I do not want to encourage in newbies
[snip much scolding ;)]
>
> While discussing some issue with the lisp-nyc RoboCup team I said,
> "Avoiding premature optimization does not mean deliberately writing slow
> code." The context was me justifying a little heavy-lifting I could have
> avoided (but experience told me the code in question had to be fast),
> but it also applies to being too lazy to choose between delete and
> remove, or nconc and append.

You did see the part where I already agreed with you, didn't you? ;)

-- 
Coby Beck
(remove #\Space "coby 101 @ big pond . com")
From: Lowell
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <bg819m$o77$1@mughi.cs.ubc.ca>
Thanks for the help everyone. I guess the answer to my question is that 
there *is not* any *single* function built into CL that would do the 
job. In one of his books, Paul Graham implements a 'filter' function 
which does exactly what I wanted but it's name (filter) is confusing 
because there are so many other filter functions out there which act 
slightly differently. Plus, I was hoping there might be something 
already built in. It seems like my original idiom can't be shortened 
without creating a utitlity function so I'm going to leave it like it was.

Lowell
From: Robert St. Amant
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <lpn65liwjy8.fsf@haeckel.csc.ncsu.edu>
Lowell <······@cs.ubc.ca> writes:

> Thanks for the help everyone. I guess the answer to my question is
> that there *is not* any *single* function built into CL that would do
> the job. In one of his books, Paul Graham implements a 'filter'
> function which does exactly what I wanted but it's name (filter) is
> confusing because there are so many other filter functions out there
> which act slightly differently. Plus, I was hoping there might be
> something already built in. It seems like my original idiom can't be
> shortened without creating a utitlity function so I'm going to leave
> it like it was.

I once had a situation in which I was using 'some', but then realized
I needed to get all of the non-nil values from a predicate applied to
the elements of a sequence, instead of just the first one.  I wrote a
function analogous to some and called it 'all', as below.  There may
be a better way of doing this, but this was quick and easy.  (It makes
an implicit assumption that you want to return a sequence of the same
type as the first input sequence.)

(defun all (predicate sequence &rest more-sequences)
  ;; As 'some' is to 'find-if', 'all' is to 'find-all-if'/'remove-if-not'.
  ;; The function 'all' iterates over one or more sequences, collecting
  ;; all values for which 'predicate' returns non-nil.
  (let ((result nil))
    (apply #'map nil #'(lambda (&rest values)
			 (let ((value (apply predicate values)))
			   (when value
			     (push value result))))
	   sequence more-sequences)
    (if (and result (not (listp sequence)))
	(coerce (nreverse result) (type-of sequence))
      (nreverse result))))

-- 
Rob St. Amant
http://www4.ncsu.edu/~stamant
From: Conrad Barski
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <a4af10cf.0307291829.5d74d756@posting.google.com>
As a Grahamite I feel it is my duty to point out:

(filter fn lst)

Which is exactly what you want and utility macro on p105 of ANSI Common Lisp.

I also have another similar function of my own:

(defun map-delta (fn lst) ;if nil, leaves unchanged - else replaces
  (mapcar (lambda (x)
                (aif (funcall fn x)
	             it
		     x))
	  lst))

Which is great for modifying only some records out of a list based on a functor.
From: Dennis Decker Jensen
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <eb3555d9.0307301047.36d3c34b@posting.google.com>
Lowell <······@cs.ubc.ca> wrote in message news:<············@mughi.cs.ubc.ca>...
> I was wondering if there was a more concise way of doing something using 
> built-in functions: Given a list lst and a function fn, I want to 
> construct a list which contains the results of applying F to memmbers of 
> L and only keeps the ones which return a true value. It is much simpler 
> in code:
> 
> (member-if identity (map #'fn lst))
> 
> I would expect that there would be soemthing built into CL

There is. I cannot believe nobody hasn't posted the most obvious answer
yet. On top of that, it's the most concise and clear solution. Forget
about loops. In my experience they're only useful for lowlevel stuff
like input/output or numerical computation (especially concerning
vectors).

(remove-if (complement #'fn) lst)

(remove-if #'oddp '(1 2 3 4 5)) => (2 4)
(remove-if (complement #'numberp '(1 a 2 b 3)) => (1 2 3)

Remember to check out the keywords :START :END :KEY and :COUNT. They
migth come in handy.

Best regards
Dennis Decker Jensen
From: Erann Gat
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <gat-3007031217450001@k-137-79-50-101.jpl.nasa.gov>
In article <····························@posting.google.com>,
·············@hotmail.com (Dennis Decker Jensen) wrote:

> Lowell <······@cs.ubc.ca> wrote in message
news:<············@mughi.cs.ubc.ca>...
> > I was wondering if there was a more concise way of doing something using 
> > built-in functions: Given a list lst and a function fn, I want to 
> > construct a list which contains the results of applying F to memmbers of 
> > L and only keeps the ones which return a true value. It is much simpler 
> > in code:
> > 
> > (member-if identity (map #'fn lst))
> > 
> > I would expect that there would be soemthing built into CL
> 
> There is. I cannot believe nobody hasn't posted the most obvious answer
> yet. On top of that, it's the most concise and clear solution. Forget
> about loops. In my experience they're only useful for lowlevel stuff
> like input/output or numerical computation (especially concerning
> vectors).
> 
> (remove-if (complement #'fn) lst)
> 
> (remove-if #'oddp '(1 2 3 4 5)) => (2 4)
> (remove-if (complement #'numberp '(1 a 2 b 3)) => (1 2 3)

It may be the most obvious answer, but it isn't correct.  (Rule of thumb:
whenever you find yourself thinking, "I can't believe no one has
discovered this obvious solution" that's a strong indication that the
"obvious" solution is wrong.)

A correct implementation of what the OP said he wanted (as Coby was first
to point out) is:

  (remove nil (mapcar #'fn lst))

I'm pretty sure that's the most concise solution also.  (delete nil ...)
works also.  Personally I like (loop for e in l when (fnn e) collect it). 
I think the extra intensional clarity more than makes up for the extra
length.


? (setf l '(0 1 2 3 4 5))
(0 1 2 3 4 5)
? (defun fnn (n) (if (oddp n) (* n n) nil))
FNN
? (remove-if (complement #'fnn) l)
(1 3 5)
? (remove nil (mapcar #'fnn l))
(1 9 25)
? (loop for e in l when (fnn e) collect it)
(1 9 25)
? 

And for good measure:

? (member-if identity (map #'fnn l))
> Error: Unbound variable: IDENTITY
> While executing: CCL::CHEAP-EVAL-IN-ENVIRONMENT
> Type Command-/ to continue, Command-. to abort.
> If continued: Retry getting the value of IDENTITY.
See the Restarts� menu item for further choices.
1 > 
Aborted
? (member-if 'identity (map #'fnn l))
> Error: Too few arguments (with opt/rest)
> While executing: MAP
> Type Command-. to abort.
See the Restarts� menu item for further choices.
1 > 
Aborted
? (member-if 'identity (mapcar #'fnn l))
(1 NIL 9 NIL 25)
?
From: Steven E. Harris
Subject: Re: looking for a more elegant solution to a simple problem
Date: 
Message-ID: <q67y8yfeq8d.fsf@raytheon.com>
·············@hotmail.com (Dennis Decker Jensen) writes:

> (remove-if #'oddp '(1 2 3 4 5)) => (2 4)
> (remove-if (complement #'numberp '(1 a 2 b 3)) => (1 2 3)

But these return a filtered list of the /original/ values, not the
values resulting from the function call. The OP wrote:

>> Given a list lst and a function fn, I want to construct a list
>> which contains the results of applying F to memmbers of L and only
>> keeps the ones which return a true value.

Note the "contains the results" clause.

-- 
Steven E. Harris        :: ········@raytheon.com
Raytheon                :: http://www.raytheon.com