Iterating over several lists at once

Carl Banks pavlovevidence at gmail.com
Wed Dec 13 13:40:50 EST 2006


Gal Diskin wrote:
> On Dec 13, 3:58 pm, Roberto Bonvallet <Roberto.Bonval... at cern.ch>
> wrote:
> > Gal Diskin wrote:
> > > Hi,
> > > I am writing a code that needs to iterate over 3 lists at the same
> > > time, i.e something like this:
> >
> > > for x1 in l1:
> > >    for x2 in l2:
> > >        for x3 in l3:
> > >            print "do something with", x1, x2, x3What's wrong with this?
> >
> > [...]
> >
> > > I'd be very happy to receive ideas about how to do this in one loop and
> > > with minimal initialization (if at all required).def cartesian_product(l1, l2, l3):
> >     for i in l1:
> >         for j in l2:
> >             for k in l3:
> >                 yield (i, j, k)
> >
> > for (i, j, k) in cartesian_product(l1, l2, l3):
> >     print "do something with", i, j, k
>
> Nothing seriously wrong, but it's not too elegent.

I wonder why you think that.  Many people here would consider it the
height of elegance to take some complicated logic, writing a function
encompassing the complicated parts, and ending up with simpler logic.
That's what happens here.

The cartesian_product solution above is not much longer than this
syntax you proposed:

for (i,j,k) in (a,b,c):
    use(i,j,k)

(You can make the function name shorter if you want.)  What is it that
you find inelegant about this solution?  Is it that you don't like
extra function sitting there?  Just put it in a module and import it.


> Especially when the
> number of lists you want to iterate over gets bigger (especially
> because of the indentation in python).

The function can be extended to allow arbitrary arguments.  Here's a
non-minmal recursive version.

def cartesian_product(*args):
    if len(args) > 1:
        for item in args[0]:
            for rest in cartesian_product(*args[1:]):
                yield (item,) + rest
    elif len(args) == 1:
        for item in args[0]:
            yield (item,)
    else:
        yield ()


> As you noticed (an phrased
> better than me), what I was wondering is if there is a way to iterate
> over the cartesian product, but without actually doing all n for loops
> but using a single "for" loop.

Even if Python had a special syntax for it, it would still be looping
internally.

And there's almost no chance of there ever being a special syntax for
it.  First of all, it's very straightforward to do with functional
solutions, as we've shown here.  Second, compare the case of zip.
Iteration in parallel (such as you would do with zip) is a lot more
common than nested iteration (cartesian_product), but even parallel
iteration doesn't have a special syntax, and th BDFL had declared that
won't in Python 3.0.

The best you could hope for is a built in function like zip, and that's
doubtful.  OTOH, something like this has decent shot of appearing in a
standard functional or iterator module of some sort.


> Thanks for replying me.

You're welcome in advance.  Also, one note about comp.lang.python
and/or python-list: it's conventional here to put replies beneath the
quoted text.  This is for the benefit of future readers, so they don't
have read conversations in reverse order.


Carl Banks




More information about the Python-list mailing list