views:

84

answers:

2

I am writing a permutation function that generate all permutations of a list in Python. My question is why this works:

def permute(inputData, outputSoFar):
    for elem in inputData:
        if elem not in outputSoFar:
            outputSoFar.append(elem)
            if len(outputSoFar) == len(inputData):
                print outputSoFar
            else:
                permute(inputData, outputSoFar) # --- Recursion
            outputSoFar.pop()

permute([1,2,3],[])

But this does not:

def permute(inputData, outputSoFar):
    for elem in inputData:
        if elem not in outputSoFar:
            outputSoFar.append(elem)
            if len(outputSoFar) == len(inputData):
                yield outputSoFar 
            else:
                permute(inputData, outputSoFar) # --- Recursion
            outputSoFar.pop()

for i in permute([1,2,3], []):
    print i

This does not work either (yield a copy of the list):

def permute(inputData, outputSoFar):
    for elem in inputData:
        if elem not in outputSoFar:
            outputSoFar.append(elem)
            if len(outputSoFar) == len(inputData):
                yield outputSoFar[:] # --- Copy of the list
            else:
                permute(inputData, outputSoFar) # --- Recursion
            outputSoFar.pop()

for i in permute([1,2,3], []):
    print i
A: 

You're destructively losing items when you do the pop. Use copies of the list instead of mutating it in-place.

Alternately, use itertools.permutations or itertools.combinations instead.

Jason Scheirer
-1: not true. Append and pop will work fine.
Eric Mickelsen
for all new code itertools.permutations is recommended
Dan D
I know about itertools.permutation. However, the purpose of this puzzle is to learn about python generator/recursion.
Thanh DK
+3  A: 

You must also yield the results of the recursive call(s):

def permute(inputData, outputSoFar):
    for a in inputData:
        if a not in outputSoFar:
            if len(outputSoFar) == len(inputData) - 1:
                yield outputSoFar + [a]
            else:
                for b in permute(inputData, outputSoFar + [a]): # --- Recursion
                    yield b

for i in permute([1,2,3], []):
    print i

... Or (closer to the OP code):

def permute(inputData, outputSoFar):
    for elem in inputData:
        if elem not in outputSoFar:
            outputSoFar.append(elem)
            if len(outputSoFar) == len(inputData):
                yield outputSoFar
            else:
                for permutation in permute(inputData, outputSoFar):
                    yield permutation # --- Recursion
            outputSoFar.pop()

for i in permute([1,2,3], []):
    print i
Eric Mickelsen
This works but I still have no idea why do I need to add yield to recursive call.
Thanh DK
Consider the first function call and the condition (if len(outputSoFar) == len(inputData), or not). The first call will fail the condition (unless there is only one element in the input), so it will yield nothing. Instead it must rely on recursive calls to find permutations, and when they do so, they will yield them. However, when they are yielded back to this first function call, it must yield them back to the original caller. (Each recursive, non-base-case call is in a similar situation.) Without this, only the leaves of the recursion tree will yield anything.
Eric Mickelsen