4

I was trying to write code in python to generate all permutations of an input string.

inp = raw_input()

out = [inp[0]]
for i in range(1,len(inp)):
   #print [range(len(j)+1) for j in out]
   out = [j[:k]+inp[i]+j[k:] for k in range(len(j)+1) for j in out]

print out

For an input 'abc', it outputs

Traceback (most recent call last): File "perm.py", line 6, in

out = [j[:k]+inp[i]+j[k:] for k in range(len(j)+1) for j in out] 

NameError: name 'j' is not defined

shell returned 1

If I uncomment line 5, and the code looks like

inp = raw_input()

out = [inp[0]]
for i in range(1,len(inp)):
   print [range(len(j)+1) for j in out]
   out = [j[:k]+inp[i]+j[k:] for k in range(len(j)+1) for j in out]

print out

For an input 'abc', it outputs

[[0, 1]]

[[0, 1, 2], [0, 1, 2]]

['cba', 'cab', 'bca', 'acb', 'bac','abc']

What sorcery is this?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Cheeku
  • 833
  • 1
  • 8
  • 22
  • @MartijnPieters But wasn't that just for the list comprehension used during the print? And if you say I have to explicitly define them, what about 'k'? – Cheeku Apr 01 '15 at 08:35
  • I meant that in your list comprehension you get a name error for `j` because it isn't assigned to yet when `range(len(j)+1)` executes, *unless* you include your `print` statement, which has a list comprehension assigning to `j`. :-) – Martijn Pieters Apr 01 '15 at 08:46

1 Answers1

3

You have your list comprehension loops back to front; you need to list them in nesting order from left to right, so your for j in out needs to come first

out = [j[:k]+inp[i]+j[k:] for j in out for k in range(len(j)+1)]

The print statement list comprehension defined a j name for your version to loop over; in Python 2, a list comprehension does not have their own scope so the for loop target is accessible after the loop completes just like with a regular for loop. This has changed in Python 3, where list comprehensions now get their own scope, just like dict and set comprehensions and generator expressions.

If it helps, write out the list comprehensions as regular loops first; your code did this:

tmp = []
for j in out:
    tmp.append(range(len(j)+1))
print tmp

out = []
for k in range(len(j)+1):
    for j in out:
        out.append(j[:k]+inp[i]+j[k:])

where the range(len(j)+1) expression only works because you have used j as a for loop target in the preceding expression building tmp for print.

You may be treating this as a coding exercise, but the standard library already includes a function that produces permutations: itertools.permutations():

from itertools import permutations

inp = raw_input()
print list(permutations(inp))

Demo:

>>> from itertools import permutations
>>> list(permutations('abc'))
[('a', 'b', 'c'), ('a', 'c', 'b'), ('b', 'a', 'c'), ('b', 'c', 'a'), ('c', 'a', 'b'), ('c', 'b', 'a')]
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Yeah, this was for illustrating list comprehensions to someone else. -_- Ended up learning something myself. – Cheeku Apr 01 '15 at 08:51