can list comprehensions replace map?

Peter Otten __peter__ at web.de
Fri Jul 29 13:11:51 EDT 2005


Andrew Dalke wrote:

> Peter Otten wrote:
>> Combining your "clever" and your "elegant" approach to something fast
>> (though I'm not entirely confident it's correct):
>> 
>> def fillzip(*seqs):
>>     def done_iter(done=[len(seqs)]):
>>         done[0] -= 1
>>         if not done[0]:
>>             return
>>         while 1:
>>             yield None
>>     seqs = [chain(seq, done_iter()) for seq in seqs]
>>     return izip(*seqs)
> 
> Ohh, that's pretty neat passing in 'done' via a mutable default argument.
> 
> It took me a bit to even realize why it does work.  :)

Though I would never have come up with it, were it not for the juxtaposition
of your two variants (I initially disliked the first and tried to improve
on the second), it is an unobvious merger :) 
It's a bit fragile, too, as
 
> Could make it one line shorter with
 
> from itertools import chain, izip, repeat
> def fillzip(*seqs):
>     def done_iter(done=[len(seqs)]):
>         done[0] -= 1
>         if not done[0]:
>             return []
>         return repeat(None)
>     seqs = [chain(seq, done_iter()) for seq in seqs]
>     return izip(*seqs)

that won't work because done_iter() is now no longer a generator.
In effect you just say

seqs = [chain(seq, repeat(None)) for seq in seqs[:-1]] + [chain(seq[-1],
[])]

I tried

class Done(Exception):
    pass

pad = repeat(None)
def fillzip(*seqs):
    def check(active=[len(seqs)]):
        active[0] -= 1
        if not active[0]:
            raise Done
        # just to turn check() into a generator
        if 0: yield None 
    seqs = [chain(seq, check(), pad) for seq in seqs]
    try
        for item in izip(*seqs):
            yield item
    except Done:
        pass

to be able to use the faster repeat() instead of the while loop, and then
stared at it for a while -- in vain -- to eliminate the for item... loop.
If there were a lazy ichain(iter_of_iters) you could tweak check() to decide
whether a repeat(None) should follow it, but I'd rather not ask Raymond for
that particular addition to the itertools.

> Now add the performance tweak....
> 
>   def done_iter(done=[len(seqs)], forever=forever, table=table)
> 
> Okay, I'm over it.  :)

Me too. I think. For now...

Peter




More information about the Python-list mailing list