[Python-Dev] A `cogen' module [was: Re: PEP 218 (sets); moving set.py to Lib]

François Pinard pinard@iro.umontreal.ca
26 Aug 2002 17:56:22 -0400


--=-=-=
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit

[François Pinard]

> Allow me some random thoughts.  [...]  Some people may think that these
> are all problems which are orthogonal to the design of a basic set feature,
> and which should be addressed in separate Python modules.

>From the received comments, I wrote a simple module reading sequences and
generating lists, instead of reading and producing sets, and taking care
of generating cartesian products, power sets, combinations, arrangements
and permutations.  I took various ideas here and there, like from previously
published messages on the Python list, and made them to look a bit alike.

The module could be called `cogen', abbreviation for COmbinatorial
GENerators.  Here is a first throw, to be criticised and improved.


--=-=-=
Content-Type: text/plain; charset=iso-8859-1
Content-Disposition: attachment; filename=cogen.py
Content-Transfer-Encoding: 8bit

#!/usr/bin/env python
# Copyright © 2002 Progiciels Bourbeau-Pinard inc.
# François Pinard <pinard@iro.umontreal.ca>, 2002-08.

"""\
Combinatorial generators.

All generators below have the property of yielding successive results
in sorted order, given than input sequences were already sorted.
"""

from __future__ import generators

def cartesian(*sequences):
    """\
Generate the `cartesian product' of all SEQUENCES.  Each member of the
product is a list containing an element taken from each original sequence.
"""
    if len(sequences) == 0:
	yield []
    else:
	first, remainder = sequences[0], sequences[1:]
	for element in first:
	    for result in cartesian(*remainder):
		result.insert(0, element)
		yield result

def subsets(sequence):
    """\
Generate all subsets of a given SEQUENCE.  Each subset is delivered
as a list holding zero or more elements of the original sequence.
"""
    yield []
    if len(sequence) > 0:
        first, remainder = sequence[0], sequence[1:]
        # Some subsets retain FIRST.
        for result in subsets(remainder):
            result.insert(0, first)
            yield result
        # Some subsets do not retain FIRST.
        for result in subsets(remainder):
            if len(result) > 0:
                yield result

def combinations(sequence, number):
    """\
Generate all combinations of NUMBER elements from list SEQUENCE.
"""
    # Adapted from Python 2.2 `test/test_generators.py'.
    if number > len(sequence):
	return
    if number == 0:
	yield []
    else:
	first, remainder = sequence[0], sequence[1:]
	# Some combinations retain FIRST.
	for result in combinations(remainder, number-1):
	    result.insert(0, first)
	    yield result
	# Some combinations do not retain FIRST.
	for result in combinations(remainder, number):
	    yield result

def arrangements(sequence, number):
    """\
Generate all arrangements of NUMBER elements from list SEQUENCE.
"""
    # Adapted from PERMUTATIONS below.
    if number > len(sequence):
	return
    if number == 0:
	yield []
    else:
	cut = 0
	for element in sequence:
	    for result in arrangements(sequence[:cut] + sequence[cut+1:],
				       number-1):
		result.insert(0, element)
		yield result
	    cut += 1

def permutations(sequence):
    """\
Generate all permutations from list SEQUENCE.
"""
    # Adapted from Gerhard Häring <gerhard.haering@gmx.de>, 2002-08-24.
    if len(sequence) == 0:
	yield []
    else:
	cut = 0
	for element in sequence:
	    for result in permutations(sequence[:cut] + sequence[cut+1:]):
		result.insert(0, element)
		yield result
	    cut += 1

def test():
    if True:
	print '\nTesting CARTESIAN.'
	for result in cartesian((5, 7), [8, 9], 'abc'):
	    print result
    if True:
        print '\nTesting SUBSETS.'
        for result in subsets(range(1, 5)):
            print result
    if True:
	print '\nTesting COMBINATIONS.'
	sequence = range(1, 5)
	for counter in range(len(sequence) + 2):
	    print "%d-combs of %s:" % (counter, sequence)
	    for combination in combinations(sequence, counter):
		print "   ", combination
    if True:
	print '\nTesting ARRANGEMENTS.'
	sequence = range(1, 5)
	for counter in range(len(sequence) + 2):
	    print "%d-arrs of %s:" % (counter, sequence)
	    for combination in arrangements(sequence, counter):
		print "   ", combination
    if True:
	print '\nTesting PERMUTATIONS.'
	for permutation in permutations(range(1, 5)):
	    print permutation

if __name__ == '__main__':
    test()

--=-=-=
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit


-- 
François Pinard   http://www.iro.umontreal.ca/~pinard

--=-=-=--