[Python-Dev] list comprehensions again...

Skip Montanaro skip@mojam.com (Skip Montanaro)
Tue, 11 Jul 2000 17:28:04 -0500 (CDT)


    BAW> Range literals I can see as being worth it.  I wonder if parallel
    BAW> for loops can't be handled more gracefully with a function -- isn't
    BAW> it just a nice wrapper around a specific call to map() anyway?

I believe Thomas proposed range literals to avoid calling the range()
function...

Regarding a wrapper around map(), you will need a pretty hairy wrapper,
since you can do some pretty complex things with Greg's version of list
comprehensions.  Here's the list comprehensions code from test_grammar.py:

    nums = [1, 2, 3, 4, 5]
    strs = ["Apple", "Banana", "Coconut"]
    spcs = ["  Apple", " Banana ", "Coco  nut  "]

    print [s.strip() for s in spcs]
    print [3 * x for x in nums]
    print [x for x in nums if x > 2]
    print [i, s for i in nums for s in strs]
    print [i, s for i in nums for s in [f for f in strs if "n" in f]]

    suppliers = [
      (1, "Boeing"),
      (2, "Ford"),
      (3, "Macdonalds")
    ]

    parts = [
      (10, "Airliner"),
      (20, "Engine"),
      (30, "Cheeseburger")
    ]

    suppart = [
      (1, 10), (1, 20), (2, 20), (3, 30)
    ]

    print [
      (sname, pname)
	for (sno, sname) in suppliers
	  for (pno, pname) in parts
	    for (sp_sno, sp_pno) in suppart
	      if sno == sp_sno and pno == sp_pno
    ]

and what it produces:

    ['Apple', 'Banana', 'Coco  nut']
    [3, 6, 9, 12, 15]
    [3, 4, 5]
    [(1, 'Apple'), (1, 'Banana'), (1, 'Coconut'), (2, 'Apple'), (2, 'Banana'), (2, 'Coconut'), (3, 'Apple'), (3, 'Banana'), (3, 'Coconut'), (4, 'Apple'), (4, 'Banana'), (4, 'Coconut'), (5, 'Apple'), (5, 'Banana'), (5, 'Coconut')]
    [(1, 'Banana'), (1, 'Coconut'), (2, 'Banana'), (2, 'Coconut'), (3, 'Banana'), (3, 'Coconut'), (4, 'Banana'), (4, 'Coconut'), (5, 'Banana'), (5, 'Coconut')]
    [('Boeing', 'Airliner'), ('Boeing', 'Engine'), ('Ford', 'Engine'), ('Macdonalds', 'Cheeseburger')]

Here's my equivalent code:

    1. map(string.strip, spcs)

    2. newnums = []
       for n in nums:
    	 newnums.append(3*n)
       return newnums

    3. newnums = []
       for n in nums:
    	 if n > 2:
    	   newnums.append(n)
       return newnums

    4. l = []
       for i in nums:
         for s in strs:
	   l.append((i,s))
       return l

    5. l = []
       fl = []
       for f in strs:
         if "n" in f:
	   fl.append(f)
       for i in nums:
         for s in fl:
	   l.append((i,s))
       return l

    6. ... the join is left as an exercise for the reader ... ;-)

It seems like fairly powerful stuff and generally tends to reduce the amount
of code the user writes.  Is it more or less understandable than the current
alternatives?  That's the $64K question.  I think in most cases it is.  I
think map and filter are more difficult to use and understand than the
example list comprehensions from the test suite, but perhaps that's just my
bias.  I was never much of a Lisper, so it took me a good year or two before
I got comfortable with anything more than the simplest uses of map.  I
probably still can't locate more than two or three instances in my own code
where I've used filter.  The nested for loops like the fourth and fifth
examples seem to stretch out vertically and require the use of temporary
variables that add complexity to the constructs.

Skip