Excellent!
My top two names are
1. multiset_permutations (reflects the mathematical name)
2. anagrams
Note that we may also want to add multiset_combinations. It hasn't been part of this discussion, but it may be part of another discussion and I wanted to point this out as I know many of you are future-conscious.
We seem to be all agreed that we want to accept "r", the length of the permutation desired.
With permutations, the *set* is passed in as a iterable representing distinct elements. With multiset_permutations, there are three ways to pass in the *multiset*:
- 1. an iterable whose elements (or an optional key function applied to which) are compared using __eq__
- 2. a dict (of which collections.Counter) is a subclass
- 3. an iterable whose elements are key-value pairs and whose values are counts
Example uses:
1. multiset_permutations(word)
2. multiset_permutations(Counter(word))
3. multiset_permutations(Counter(word).items())
From a dictionary:
1. multiset_permutations(itertools.chain.from_iterable(itertools.repeat(k, v) for k, v in d.items()))
2. multiset_permutations(d)
3. multiset_permutations(d.items())
From an iterable of key-value pairs:
1. multiset_permutations(itertools.chain.from_iterable(itertools.repeat(k, v) for k, v in it))
2. multiset_permutations({k: v for k, v in it})
3. multiset_permutations(it)
The advantage of 2 is that no elements are compared by multiset_permutations (so it is simpler and faster).
The advantage of 3 is that no elements are compared, and they need not be comparable or hashable. This version is truly a generalization of the "permutations" function. This way, for any input "it" you could pass to permutations, you could equivalently pass zip(it, itertools.repeat(1)) to multiset_permutations.
Comments?
Neil