[Tutor] List comprehension

Kirby Urner urnerk@qwest.net
Tue, 22 Jan 2002 13:02:11 -0800


I think of list comprehensions as replacing syntax like
this:

    def lc(f,listb):
       output = []
       for a in listb:
           output.append(f(a))
       return output

E.g.:

   >>> def f(x):  return x*x

   >>> lc(f,[1,2,3,4,5])
   [1, 4, 9, 16, 25]

The lambda form would be:

    map(lambda a: expr(a), listb)

    >>> map(lambda x: x*x, [1,2,3,4,5])
    [1, 4, 9, 16, 25]

Using list comprehension syntax, we write:

    [expr(a) for a in listb]

E.g.:

   >>> [x*x for x in [1,2,3,4,5]]
   [1, 4, 9, 16, 25]

Since we've already specified expr(a), there's no
need to use a colon i.e. we don't go:

    [for a in listb:  expr(a)]

which would also be expressive.

But putting expr(a) first is more like
map(lambda a: expr(a),list) in that it shows *what* you
plan to do before you specify *over what* list you plan
to iterate.

Then there's the filtering 'if' clause.

As an application I really like defining gcd(a,b) and then
gathering totatives (positives relatively prime to N and < N)
by going:

  >>> def gcd(a,b):
	 while b:
	    a,b = b, a%b
	 return a

  totatives = [t for t in range(1,N) if gcd(t,N)==1]

E.g.:

   >>> totatives = [t for t in range(1,28) if gcd(t,28)==1]
   >>> totatives
   [1, 3, 5, 9, 11, 13, 15, 17, 19, 23, 25, 27]

The lambda form would be:

   >>> filter(lambda a: gcd(a,28)==1,range(28))
   [1, 3, 5, 9, 11, 13, 15, 17, 19, 23, 25, 27]

I don't find that any clearer than the list comprehension
way of doing it.

Finally, there's the ability to stack for clauses, effectively
nesting them:

   >>> [a+b for a in ['a','b','c'] for b in ['c','d','e']]
   ['ac', 'ad', 'ae', 'bc', 'bd', 'be', 'cc', 'cd', 'ce']

That suggests a generic way of multiplying two integers:

   >>> reduce(add,[a*b for a in [400,20,3] for b in [300,20,1]])
   135783
   >>> 423*321
   135783

This shows that we can break two numbers into lists of terms
which sum to the original numbers, and by adding all
combinations (think of an n x n table), we get the product.
You don't even need to break the two numbers into the same
number of terms:

 >>> reduce(add,[a*b for a in [420,3] for b in [300,10,10,1]])
135783

OK, now I'm really getting into Useless Python territory.
But if you imagine kids in 7th or 8th grade, learning
Python and math at the same time, you can see how insights
such as the above might come in handy.

Kirby