Pat a écrit :
I have written chunks of Python code that look this:

    new_array = []
    for a in array:
        if not len( a ):
            continue
        new_array.append( a )


# à la lisp
new_array = filter(None, array)

# à la haskell
new_array = [a for a in array if a]

NB : all builtin empty builtin sequence type have a false boolen value, so 'not len(a)' and 'not a' are usually practicaly equivalent.

<ot>
While we're at it : I assume you mean 'list', not 'array'. That's at least how the builtin type is named...
</ot>


and...

    string = ""
    for r in results:
        if not r.startswith( '#' ):
            string =+ r


# à la lisp
string = "".join(filter(lambda s: not s.startwith('#'), results)

# à la haskell
string = "".join(r for r in results if not s.startswith('#'))

It seems that a list comprehension could clean up the code, but I seem to have a mental block on list comprehensions. I've read up a lot on this subject in my books and on the Internet and for whatever reason, I'm having problems with this idiom (if that's the correct expression).

I've made a number of attempts to solve this on my own but I keep getting errors.

Could someone please tell me how I could convert the above code to something more elegant but readily understandable?

Basically, a list expression is written as (pseudo BNF):

'['<expression> for item in <iterable_expression> [if <conditional_expression>] ']'


where
- <expression> is any Python expression
- <iterable_expression> is any Python expression that evals to an iterable
- the 'if <conditional_expression>' part is optional

The resulting list will be obtained by evaluating <expression> for each item of <iterable_expression> for which <conditional_expression> is verified.

Since the main goal of list expressions is to build, well, lists, one usually uses an <expression> that evals to something we possibly want to be part of the new list !-)


Now for something more practical:

First let's rewrite your first snippet to get rid of the continue statement:

new_list = []
for item in source_list:
    if len(item):
        new_list.append(item)


And let's rewrite the second one to replace string concatenation with the more idiomatic (and much more flexible) list / join idiom:

new_list = []
for item in results:
    if not item.startswith('#'):
        new_list.append(item)

string = "".join(new_list)


You may start to see a kind of pattern here:

0.  new_list = []
1.  for item in sequence:
2.     if condition(item):
3.         new_list.append(item)

The equivalent list comprehension is:

new_list = [item for item in sequence if condition(item)]


Since you didn't apply any transformation to 'item' in your for loops, the <expression> part is as simple as it can be : we want the item, period.

The <iterable_expression> is here again quite simple : it's your source sequence. Ditto for the the <conditional_expression>.

IOW, your for-loop pattern is really:

new_list = []
for item in <iterable_expression>:
    if <conditional_expression>:
        new_list.append(<expression>)


Once you've identified the different parts, translating this for-loop to a list comprehension is mostly a matter of reording.


Finally, if someone could point me to a good tutorial or explain list compressions I would be forever in your debt.

http://en.wikipedia.org/wiki/List_comprehension
http://en.wikipedia.org/wiki/Set-builder_notation


HTH
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to