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
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/
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)
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
"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
"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
"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
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
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.
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
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