[Python-ideas] Function to unnest for-statements
Arnaud Delobelle
arnodel at googlemail.com
Sun May 25 10:01:39 CEST 2008
On 23 May 2008, at 08:15, Carl Johnson wrote:
> This is based off of http://boredzo.org/blog/archives/2007-10-22/generating-all-combinations
> . It's also discussed at http://reddit.com/info/5ywrs/comments/ and
> someone with a lot of spare time can read Knuth's fascile on the
> subject at http://www-cs-faculty.stanford.edu/~knuth/fasc2a.ps.gz .
>
> OK, suppose you have a situation where you need to loop through all
> combinations of 3 lists. The simple solution is to write three
> nested for-loops:
>
> for x in xs:
> for y in ys:
> for z in zs:
> #Do something
>
> Of course, this is obvious O(n^3), which is bad, but sometimes you
> don't have a choice. However, what if (to use an example I ran
> into), you're making up a truth table. If you know you have three
> variables, you might write:
>
> for a in [True, False]:
> for b in [True, False]:
> for c in [True, False]:
> print a, b, c
>
> Which yields
>
> True True True
> True True False
> True False True
> True False False
> False True True
> False True False
> False False True
> False False False
>
> But if you don't know how many variables you'll have, you're stuck
> writing a complicated function instead of using a nice, simple for-
> loop.
>
> So, going back to the first example, wouldn't it be nicer to write:
>
> for x, y, z in combine(xs, ys, zs):
> #Do something
>
> If nothing else, this has the advantage that if you don't have to
> nest the for-loops, things don't end up being so deeply indented on
> the screen.
>
> Similarly, a general truth table maker can be written:
>
> ts_and_fs = [[True, False]] * num_vars
> for variables in combine(*ts_and_fs):
> print variables
>
> So, I think a "combine" function (or some other, better name) would
> be a good thing to have, at least in the itertools, if not as a
> built-in function. How to implement it? Well, probably it should be
> done in C for efficiency, but of the versions done http://boredzo.org/blog/archives/2007-10-22/generating-all-combinations
> the one that I like best is
>
> def combine(*sequences):
> #Test to guard against infinite recursion
> if sequences:
> def _inner(*seqs):
> if len(seqs) == 1:
> for i in seqs[0]: yield (i, )
> else:
> for rest in combine(*seqs[:-1]):
> for i in seqs[-1]:
> yield rest + (i, )
> return _inner(*sequences)
> else:
> #Empty generator that yields StopIteration
> def _inner():
> return
> yield
> return _inner()
>
> However, I'm also convinced that there is a more efficient way to do
> this'
I don't understand why you define those _inner() generator functions.
Anyway, here's a version that doesn't use recursion:
def product(*sequences):
if not sequences: return
i, imax = 0, len(sequences)-1
val = [None]*(imax+1)
iters = [iter(seq) for seq in sequences]
while i >= 0:
for val[i] in iters[i]:
if i == imax:
yield tuple(val)
else:
i += 1
break
else:
iters[i] = iter(sequences[i])
i -= 1
>
> So, what do you think? Is this a common enough need that it should
> be built into itertools? The main namespace? Or should we leave it
> out, since adding it would encourage people writing O(n^x)
> algorithms? If nothing else, list members can enjoy rewriting this
> function for fun.
It is already in itertools:
Python 3.0a4+ (py3k:62388, Apr 19 2008, 15:34:00)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from itertools import product
>>> for v in product(*[[True, False]]*3):
... print(v)
...
(True, True, True)
(True, True, False)
(True, False, True)
(True, False, False)
(False, True, True)
(False, True, False)
(False, False, True)
(False, False, False)
>>>
--
Arnaud
More information about the Python-ideas
mailing list