Pythonic idioms / Programming Puzzle
Alex Martelli
aleax at aleax.it
Sat Jan 19 14:30:43 EST 2002
ameoba wrote:
...
> permutations of an arbitrary list of arbitrarily long lists of strings.
Actually, I don't see where permutations enter the picture (every item
stays in place -- permutation is changing order), but I think I get what
you mean (from your examples below) -- given a list of lists, L, return
the list of all lists, R, such that, for each list r in R, r[i] is an item
from L[i] for each valid i.
> Looking at these two fundamentally different solutions to the same problem
> got me thinking... Obviously, both of these solutions are correct and
> fully functional, but is one a more Pythonic way of solving the problem,
> or is there an even more Pythonic solution floating arround?
I like your solutions, and I think the recursive one is more Pythonic since
it's so much simpler, although I do personally prefer to consider such
enumeration problems in terms of "counting in multiple bases", as you
do in the second case.
Here's what I consider a somewhat more Pythonic dressing for the
recursive solution:
def choices(menu, sofar = []):
if menu:
result = []
for x in menu[0]: result.extend(choices(menu[1:], sofar+[x]))
return result
else:
return [" ".join(sofar)]
Here's a traditional "recursion elimination" over this by introduction
of an auxiliary stack:
def choices(menu, sofar = []):
result = []
stack = []
while 1:
if menu:
for x in menu[0]:
stack.append((menu[1:], sofar+[x]))
else:
result.append(" ".join(sofar))
if stack: menu, sofar = stack.pop()
else: break
result.reverse()
return result
and here's a stranger approach, based on getting the resulting list's
structure right off the bat and then filling-in its slots:
def choices(menu):
import operator
totlen = reduce(operator.mul, map(len, menu))
result = [ menu[:] for j in range(totlen)]
for i in range(len(menu)):
j = 0
reps = totlen/len(menu[i])
while j < len(result):
for item in menu[i]:
for k in range(reps):
result[j][i] = item
j += 1
totlen = reps
return map(" ".join, result)
I think this latest is least Pythonic, being the most complex approach.
> Another question; Does my code show that I have any perceptable 'accent'?
> That is to ask, am I obviously influenced by the other languages I've
> worked in (mostly C/Java/C++)?
Only marginally and superficially, in strange choice of parentheses and
spaces, e.g.:
> if(menu == []) : print sofar.strip()
I don't think the redundant parentheses after the if would occur to somebody
without a C-influenced-language syntax's background; and most Pythonic
would be to test menu directly (if menu or if not menu, rather than as you
did if menu==[] or, as some other C-influenced programmers do, if len(menu)
and so on). But this is little more than syntax-sugar anyway.
The only somewhat substantial Pythonic instinct you're still lacking seems
to be -- don't build a string by repeated + or += (quite slow), rather put
the pieces in a list (by append, extend, +, +=, or other ways yet) and when
you're done ''.join it (or in this case ' '.join since we want separating
spaces, apparently) back into a string. The performance difference gets
so huge so fast that an experienced Pythonista always things in terms of the
latter idiom.
Alex
More information about the Python-list
mailing list