views:

49

answers:

1

As an exercise, I've been trying out various ways of generating all permutations of a list in Python -- recursive, non-recursive... -- and comparing the performance with itertools.permutations(). But I'm having trouble with the generator version of the recursive method, which doesn't finish cleanly with a StopIteration exception, but instead throws an IndexError:

def spawnperms(alist):
    """same algorithm as recursive option, but a generator"""
    if (alist == []):
        yield []
    for perm in spawnperms(alist[:-1]):
        for i in range(len(perm)+1):
            yield perm[:i] + [alist[-1]] + perm[i:]

Calling this from the Python interpreter:

>>> for i in spawnperms(range(3)):
...     print i
... 
[2, 1, 0]
[1, 2, 0]
[1, 0, 2]
[2, 0, 1]
[0, 2, 1]
[0, 1, 2]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in spawnperms
  File "<stdin>", line 5, in spawnperms
  File "<stdin>", line 5, in spawnperms
  File "<stdin>", line 7, in spawnperms
IndexError: list index out of range

Ouch. I tried stepping through it with pdb, which nearly created a stack overflow in my brain, but what I understood is that the recursion "reaches down" to the empty list, and then the outer (I think) for loop runs out of indices.

How can I correct my code?

EDIT: One learning from Mark Byers' deceptively simple correct answer is that clean coding practices can prevent mistakes. Had I used an else systematically after if, regardless of whether I thought the condition could ever be revisited, this wouldn't have happened. And it still feels very stupid!

+6  A: 

You are missing an else:

if (alist == []):
    yield []
else:
    for ...

This is because yield does not behave in the same way as return. Execution continues after the yield statement when you request the next value.

Mark Byers
Hmmmmmm... the difference between `yield` and `return` is precisely why I thought I didn't need the `else`. But I think my logic is flawed.
chryss
:) yes, my logic was entirely flawed. Many thanks -- how stupid I feel now.
chryss