Home> Blog> Solution to Sets and Dictionaries Exercise

Solution to Sets and Dictionaries Exercise

This post originally appeared on the Software Carpentry website.

Last week, I posted an exercise on working with sets and dictionaries that also included a fair bit of file I/O and string manipulation. My solution is below, in four parts, along with the code produced in each. If someone would like to re-do the file parsing using regular expressions, I'd be happy to post that as well.

import sys

#--------------------

def parse_pair(pair):
    '''
    Parse an atom-count pair.  If the count is missing, assume
    that the count value is 1.
    '''
    if '*' not in pair:
        return pair, 1
    atom, count = pair.split('*')
    count = int(count)
    return atom, count

#--------------------

def parse_molecule(text):
    '''
    Get a single molecule description from a text string.
    '''
    name, formula_text = text.split(':')
    name = name.strip()
    pairs = formula_text.strip().split('.')
    formula = {}
    for p in pairs:
        atom, count = parse_pair(p)
        assert atom not in formula, \
               'Already seen atom %s in text %s' % (atom, text)
        formula[atom] = count
    return name, formula

#--------------------

def read_molecules(reader):
    '''
    Read molecules from a molecule file, returning a dictionary
    of {name : formula} pairs.
    '''
    result = {}
    for line in reader:
        line = line.strip()
        if (not line) or line.startswith('#'):
            continue
        name, formula = parse_molecule(line)
        assert name not in result, \
               'Already seen %s!' % name
        result[name] = formula
    return result

#--------------------

print read_molecules(sys.stdin)

Part 2

{% assign video_title="Software Carpentry Sets" %} {% assign video_slug="44QqEyCylxc" %} {% assign video_time="" %} {% include youtube %}
def merge(left, right):
    result = {}

    for key in left:
        # Only in left
        if key not in right:
            result[key] = left[key]
        # In both, so check that values are the same.
        else:
            if left[key] == right[key]:
                result[key] = left[key]

    for key in right:
        # Only in right.
        if key not in left:
            result[key] = right[key]

    return result

Part 3

{% assign video_title="Software Carpentry Sets" %} {% assign video_slug="QPs7Jm2hgk4" %} {% assign video_time="" %} {% include youtube %}
import sys
from nano import read_molecules

#--------------------

def can_produce(formulas, atom):
    '''
    Return the set of molecules that contain the given atom.
    '''
    result = set()
    for molecule in formulas:
        if atom in formulas[molecule]:
            result.add(molecule)
    return result

#--------------------

if __name__ == '__main__':
    data = read_molecules(sys.stdin)
    atom = sys.argv[1]
    print can_produce(data, atom)

Part 4

{% assign video_title="Software Carpentry Sets" %} {% assign video_slug="EkYNjWljErc" %} {% assign video_time="" %} {% include youtube %}
import sys
from nano import read_molecules
from merge import merge
from produce import can_produce

#--------------------

def get_data(filename):
    if len(filenames) == 0:
        data = read_molecules(sys.stdin)
    else:
        data = {}
        for f in filenames:
            reader = open(f, 'r')
            more_data = read_molecules(reader)
            reader.close()
            data = merge(data, more_data)
    return data

#--------------------

if __name__ == '__main__':
    assert len(sys.argv) >= 2, 'Usage: final.py atom [files...]'
    atom_name = sys.argv[1]
    filenames = sys.argv[2:]
    data = get_data(filenames)
    makeable = can_produce(data, atom_name)
    makeable = list(makeable)
    makeable.sort()
    for m in makeable:
        print m