combinations of variable length nested lists
Terry Reedy
tjreedy at home.com
Tue Aug 7 19:19:50 EDT 2001
You are asking for multiple crossconcatenation: l[0] * l[1] * ...
*l[n-1]
(using * loosely here). Grouping this as l[0] * (l[1] *
(...(l[n-2]*l[n-1])...) puts the combos in 'lexicographic' order: l[0]
varies slowest, l[n-1] varies fastest. The recursive formulation
cc(ll) = ll[0] * cc(ll[1:]) leads directly to Just's program.
An alternative to building the output more or less in parallel is to
build each combo one at a time in a worklist:
def crosscat(lseq):
if not lseq: return []
n = len(lseq)
worklist = [0]*n
func = ''.join # return combos as string if all seqs are,
else tuple
for seq in lseq:
if type(seq) != type(''):
func = tuple; break
result = []
global _cc_ # delete with nested scoping
def
_cc_(k,lseq=lseq,n=n,worklist=worklist,func=func,respend=result.append
):
global _cc_
if k == n:
respend(func(worklist))
else:
for item in lseq[k]:
worklist[k] = item
_cc_(k+1)
_cc_(0)
return result
This version is trivially turned into generator by replacing first
return and respend with yield and removing result and second return.
This pattern of initializing and then defining and calling an inner
function for the actual recursion is fairly common. I also used it
for the partition problem of a few weeks ago. Based on my experience
with that problem, I would expect an iterative version to be about 30%
faster.
Terry J. Reedy
More information about the Python-list
mailing list