Reputation: 33827
Here is a function that iterates over permutations of letters in a string:
def permutations(items):
for x in _permutations_rec('', items, len(items)):
yield x
def _permutations_rec(current, items, n):
if len(current) == n:
yield current
else:
for item, other_items in pick_item(items):
for next_ in _permutations_rec(current+item, other_items, n):
yield next_
def pick_item(items):
for i, item in enumerate(items):
yield item, items[:i] + items[i+1:]
# print permutations
for x in permutations('abc'):
print x
In _permutations_rec
in else
part, I have two loops. In the first one I
pick the next item that I append to the current
string. The second loop
iterates the next partial results and yields them. So, the second for
is only to
handle the iterator for the recursive call and "bubble-up" its results.
I have found this pattern frequently when yielding results from recursive calls, e.g. when working with backtracking.
Question:
Is there an idiomatic, elegant way to use only one loop there, instead of two? Although I know there is nothing wrong there with those two loops, maybe there is some iterator kung-fu that would allow me to use only one (the simpler, the better).
Edit:
itertools.permutations
, my permutations
is just a toy example Upvotes: 1
Views: 276
Reputation: 18859
A shorter version of your, almost the same:
In [1]: def permutations(items):
....: if len(items) == 0:
....: yield items
....: else:
....: for n in range(len(items)):
....: for ps in permutations(items[:n]+items[n+1:]):
....: yield ps + items[n:n+1]
....:
In [2]: list(permutations('abc'))
Out[2]: ['cba', 'bca', 'cab', 'acb', 'bac', 'abc']
In [3]: list(permutations(list('abc')))
Out[3]:
[['c', 'b', 'a'],
['b', 'c', 'a'],
['c', 'a', 'b'],
['a', 'c', 'b'],
['b', 'a', 'c'],
['a', 'b', 'c']]
BTW: The equivalent code in Scala as following:
scala> def perm[T](xs: List[T]): Iterator[List[T]] = xs match {
| case Nil => Iterator(Nil)
| case _ => for(x <- xs.iterator; ys <- perm(xs diff Seq(x))) yield x::ys
| }
perm: [T](xs: List[T])Iterator[List[T]]
scala> val itr = perm(List.range(0,100))
itr: Iterator[List[Int]] = non-empty iterator
scala> itr.next
res0: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99)
scala> perm(1::2::3::Nil) foreach println
List(1, 2, 3)
List(1, 3, 2)
List(2, 1, 3)
List(2, 3, 1)
List(3, 1, 2)
List(3, 2, 1)
Upvotes: 0
Reputation: 6828
Because "simple is better than complex", you can simply use itertools.permutations:
from itertools import permutations
for p in permutations('abc'):
print(p)
Output:
('a', 'b', 'c')
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')
If you still want to use your function, yo can use the new python3's yield from
statement as @DSM explained.
Upvotes: 3
Reputation: 353059
In modern Python, you can use yield from to avoid the innermost loop. Time to upgrade? :^)
for item, other_items in pick_item(items):
yield from _permutations_rec(current+item, other_items, n)
Upvotes: 4