[Tutor] Re: List Comprehensions again [an example with filter()]

Danny Yoo dyoo@hkn.eecs.berkeley.edu
Fri, 25 Jan 2002 19:19:28 -0800 (PST)


On Thu, 24 Jan 2002, Erik Price wrote:

> > i came to python before list comprehensions.  it took me A LONG TIME to
> > understand map/filter/lambda... actually i'll say i still don't really
> > understand them.
> >
> > when i first saw list comprehensions, i thought "I have NO CLUE what
> > this is doing".  then i started playing.  i took a map/lambda
> 
> (snip)
> 
> 
> As a superfledgling, I have to say that I have no idea what you guys are 
> talking about.  For the first few messages I figured it was something 
> that I could pick up later on.  Now this thread has gone on for several 
> days and I'm starting to wonder if I should learn this lambda / list 

You can pick it up later.  Basically, list comprehensions, maps, and
filters are fancy ways of doing list processing.  Often, we're looking at
a list --- a sequence of things --- and we might want to do some mass
action to that whole sequence.  Comprehensions, map(), and filter() are
all convenient ways of doing operations on whole sequences.


Let's say, for example, that we have a list of words, and we'd like to
figure out which words end with an 's'.  If we've never heard of
comprehensions, maps, or filters, we can still write something that'll
help us find those s-words:

###
for w in words:
    if w[-1] == 's':
        print w
###

is an example of something we can do.  If we wanted to be fancy, we could
even do something like this:

###
def endsWithS(word):
    return word[-1] == 's'

for w in words:
    if endsWithS(w):
        print w
###


What people have found out after working with lists is that there's often
common patterns or things we do with lists.  It's a very common thing to
take a list, and fish out "good" elements out of that list.


Let's take another example: if we had a list of words, it might be nice to
grab all the words that are spelled the same, forwards and backwards:

###
def reverse(word):
    letters = list(word)
    letters.reverse()
    return string.join(letters, '')


def isPalindrome(word):
    reversed_word = reverse(word)
    return reversed_word == word


def filterForPalindromes(words):
    palindromes = []
    for w in words:
        if isPalindrome(w):
            palindromes.append(w)
    return palindromes
###



Let's see how this might work:

###
>>> filterForPalindromes(['pattern', 'dad', 'ocelot', 'pop'])
['dad', 'pop']
###


What the filter() function does is duplicate the behavior of
filterForPalindromes(), but with a special twist: it leaves what it means
to be a "good" elements out of its definition, and allows us to tell it
what a good element should be!  Here's how we'd grab all the palindromes
if we used the filter() function:

###
>>> filter(isPalindrome, ['pattern', 'dad', 'ocelot', 'pop'])
['dad', 'pop']
###

It's not surprising that we get the same answer... but it IS surpising
that we did all that without writing a single loop ourselves.  And that's
pretty neat!  All we need to do is give filter() the notion of what a good
element is, and that's why we pass filter the function 'isPalindrome()'.  


You probably already feel comfortable passing around numbers and strings
to functions, but other functions?  If this is the first time you've seen
it, it will probably seem pretty darn weird at first, but it'll make sense
as you play with it.


In summary, it's not that we can't do without list comprehensions or map()
or filter() --- we can --- but they are very convenient functions to know
about, and they often free us from worrying about iterating manually
through a list.


If you have questions, please feel free to ask.