List comprehensions

Alexander Williams thantos at chancel.org
Tue Dec 21 22:58:16 EST 1999


On 21 Dec 1999 17:21:27 GMT, Albert Hofkamp <hat at se-46.wpa.wtb.tue.nl>
wrote:
>Say for yourself:
>
> *[ x<0 -> x:=x+2 
>  | x>0 -> x:=x-3
>  ]
>
>is much nicer than
>
> while x<0 or x>0:
>   if x<0:
>     x:=x+2
>   elif x>0:
>     x:=x-3
>
><big smile>

Hmmm, am I to assume that the *[] syntax causes it to return a curried
function that takes a list of things as its argument and returns a
list of things as its result?  If so, then your while example in
Python there is a bit flawed ...

    >>> def transformer(x):
    >>>     if x==0:
    >>>         return None
    >>>     elif x<0:
    >>>         return x+2
    >>>     else return x-3
    >>> 
    >>> filter(None,
    >>>        map(transformer, inputList))

Note that I put in the filter to catch the cases when transformer()
returns None when x is 0.  Its not really as pretty as your syntax,
no, but that's why List Comprehensions are so appealing to the
Pythonic crowd: we're pretty-obsessive.  :)  

Now, if only we could get automatic currying ...  :)

>and parallel, you get
>  [ (1,3), (2,4) ]

Oooh, you want to zip them together; that's a different thing
altogether (and not really something I see Comprehensions as being
necessary for).  In the case above you'd have:

    >>> zip(xs, ys)

... and get exactly the list you wanted; once you have zipped lists
together, you can then feed them to a Comprehension.

(And, if you wanted to do it without adding anything to Python, you
can do:

    >>> map(None, xs, ys)

... which is a perfectly valid use for map (and a good reason to keep
it around).)

>>    sumGreaterThanFive(lst) = foldl(operator.plus,
>>                                    [e <- lst, e>5])
>
>In our language, you cannot write an expression just like that. You'd
>have to define a full function for it.

Works in Python, though (or it will, once we have Comprehensions in
place ... wonder if I can push for infinite lists, too? ...)  :)

>I think that many users never use it, because it is too much hassle to
>have to write a separate function. They rather write a for-loop instead.

We can lay this at the feet of the rather kludgy lambda abstractionin
Python; much as I love lambda, sometimes you need more *power*.
Secondary to that thought, however, is the idea that a wholly seperate
function can make the intent of that piece of code clearer.

>  [ x+3 | y <- ys, z <- zs, y>z, x <- xs, y+z<x ]
>
>and define separate functions for each level.
>(anyone care to do this with map and filter ?)

Let's see ...  Not really easily with map and filter, because quite
honestly they're intended to be soley parallel iterators (fulfilling
what you asked for above), but in basic Python, you have:

    >>> xs = range(1, 11)
    >>> ys = range(1, 11)
    >>> zs = range(1, 11)
    >>> 
    >>> retList = []
    >>> 
    >>> for x in xs:
    >>>     for y in ys:
    >>>         for z in zs:
    >>>             if (y>z) and ((y+z)<x):
    >>>                 retList.append(x+3)

I needed to give myself some xs and ys to work on, and the fact that
Python and Haskell's range(n, m) and [n..m] iterators function
differently at the end of the range threw me for a bit.  Hard to get
brains to shift gears.

Does the List Comprehension version look nicer?  Undoubtedly.
Interestingly, though, with a wee bit of juggling, its structure looks
very similar, like so:

    Haskell:
    >>> retList = [x+3 | y <- ys, 
    >>> 	         z <- zs, y>z, 
    >>> 	         x <- xs, y+z<x]

... mirrors the (slightly reorganized) ...

    Python:
    >>> for y in ys:
    >>>     for z in zs:
    >>>         if y>z:
    >>>            for x in xs:
    >>>                if (y+z)<x:
    >>>                   retList.append(x+3)

Interestingly, they have the same structure in both the list
expansions and the tests.

My own intuitive feeling is that a List Comprehension should return a
/list/ (even if its an empty list or a singleton list), while if you
want a single value to emerge, you need a function.  Now, this begs
the question of those times a list /is/ the value and when its a list
of values ...

-- 
Alexander Williams (thantos at gw.total-web.net)           | In the End,
  "Join the secret struggle for the soul of the world." | Oblivion
  Nobilis, a new Kind of RPG                            | Always
  http://www.chancel.org                                | Wins



More information about the Python-list mailing list