0
def read_poetry_form_description(poetry_forms_file):
    """ (file open for reading) -> poetry pattern

    Precondition: we have just read a poetry form name from poetry_forms_file.

    Return the next poetry pattern from poetry_forms_file.
    """
    # Create three empty lists
    syllables_list = []
    rhyme_list = []
    pattern_list = []
    # Read the first line of the pattern
    line = poetry_forms_file.readline()
    # Read until the end the the pattern
    while line != '\n' and line != '':
        # Clean the \n's
        pattern_list.append(line.replace('\n', '').split(' '))
        line = poetry_forms_file.readline()
    # Add elements to lists
    for i in pattern_list:
        syllables_list.append(int(i[0]))
        rhyme_list.append(i[1])
    # Add two lists into a tuple
    pattern = (syllables_list, rhyme_list)
    return pattern

def read_poetry_form_descriptions(poetry_forms_file):
    """ (file open for reading) -> dict of {str: poetry pattern}

    Return a dictionary of poetry form name to poetry pattern for the
    poetry forms in poetry_forms_file.
    """
    # Initiate variables
    forms_dict = {}
    keys = []
    values = []
    # Get the first form
    line = poetry_forms_file.readline()
    # Add the name to the keys list
    keys.append(line.replace('\n', ''))
    # Add the variable to the values list using the previous function
    values.append(read_poetry_form_description(poetry_forms_file))
    while line != '':
        # Check if the line is the beginning of a form
        if line == '\n':
            line = poetry_forms_file.readline()
            keys.append(line.replace('\n', ''))
            values.append(read_poetry_form_description(poetry_forms_file))
        else:
            line = poetry_forms_file.readline()
    # Add key-value pairs to the dictionary
    for i in range(len(keys)):
        forms_dict[keys[i]] = values[i]
    return forms_dict

So the problem occurs when I tried to test my code using the text file. It returns the following: read_poetry_form_descriptions(open('poetry_forms.txt'))

{'Limerick': ([8, 8, 5, 5, 8], ['A', 'A', 'B', 'B', 'A']), 'Rondeau': ([8, 8, 8, 8, 8, 8, 8, 8, 4, 8, 8, 8, 8, 8, 4], ['A', 'A', 'B', 'B', 'A', 'A', 'A', 'B', 'C', 'A', 'A', 'B', 'B', 'A', 'C']), 'Haiku': ([5, 7, 5], ['', '', '*'])}

Which is supposed to have another two key-value pairs. This is what's in the text file:

Haiku
5 *
7 *
5 *

Sonnet
10 A
10 B
10 A
10 B
10 C
10 D
10 C
10 D
10 E
10 F
10 E
10 F
10 G
10 G

Limerick
8 A
8 A
5 B
5 B
8 A

Quintain (English)
0 A
0 B
0 A
0 B
0 B

Rondeau
8 A
8 A
8 B
8 B
8 A
8 A
8 A
8 B
4 C
8 A
8 A
8 B
8 B
8 A
4 C
Marogbee
  • 13
  • 3

3 Answers3

0

The problem is because you seem to be treating "line" as a global variable but it isn't global. You can easily "fix" this by making it global; however, this is terrible practice.

EDIT: I have updated your code to work without globals. The issue is that the local variable line isn't automatically synced when you read from the file, so your last line read in one function won't update the line variable in another function. Also, take a look at string handling like split and strip.

def read_poetry_form_description(poetry_forms_file):
    """ (file open for reading) -> poetry pattern

    Precondition: we have just read a poetry form name from poetry_forms_file.

    Return the next poetry pattern from poetry_forms_file.
    """
    # Create three empty lists
    syllables_list = []
    rhyme_list = []
    pattern_list = []
    # Read the first line of the pattern
    line = poetry_forms_file.readline()
    # Read until the end the the pattern
    while line != '\n' and line != '':
        # Clean the \n's
        pattern_list.append(line.replace('\n', '').split(' '))
        line = poetry_forms_file.readline()
    # Add elements to lists
    for i in pattern_list:
        syllables_list.append(int(i[0]))
        rhyme_list.append(i[1])
    # Add two lists into a tuple
    pattern = (syllables_list, rhyme_list)
    return pattern

def read_poetry_form_descriptions(poetry_forms_file):
    """ (file open for reading) -> dict of {str: poetry pattern}

    Return a dictionary of poetry form name to poetry pattern for the
    poetry forms in poetry_forms_file.
    """
    # Initiate variables
    forms_dict = {}
    keys = []
    values = []
    # Get the first line
    line = poetry_forms_file.readline()
    while line != '':
        # Check if the line is the beginning of a form
        if line != '\n':
            keys.append(line.replace('\n', ''))
            values.append(read_poetry_form_description(poetry_forms_file))
        line = poetry_forms_file.readline()
    # Add key-value pairs to the dictionary
    for i in range(len(keys)):
        forms_dict[keys[i]] = values[i]
    return forms_dict
doog abides
  • 2,270
  • 16
  • 13
0

The problem is that read_poetry_form_descriptions recognises '\n' as the beginning of a form description. But read_poetry_form_description also uses '\n' to recognise the end of a form description. So when it passes control back to read_poetry_form_descriptions, the blank line has already been read.

There are various ways to solve this but I would actually find it cleaner to reorganise and simplify the code in a single function like this:

def read_poetry_form_descriptions(poetry_forms_file):
    forms = {}
    title = None
    for line in poetry_forms_file:
        if line == '\n':
            forms[title] = syllables, rhymes
            title = None
        elif title == None:
            title = line.strip()
            syllables = []
            rhymes = []
        else:
            syllable, rhyme = line.strip().split()
            syllables.append(syllable)
            rhymes.append(rhyme)
    return forms

EDIT: If as you say in the comment, you have to keep both functions, then you can alter the 2nd one as follows.

def read_poetry_form_descriptions(poetry_forms_file):
    forms = {}
    while True:
        line = poetry_forms_file.readline()
        if line == '':
            return forms
        forms[line.strip()] = read_poetry_form_description(poetry_forms_file)

This function should not check for '\n', because the other function is taking care of that.

Stuart
  • 9,597
  • 1
  • 21
  • 30
  • This is an assignment, both functions will be used in another python file. So I don't think simplifying the code would be the best way here, but thank you anyway. – Marogbee Nov 25 '14 at 22:47
0

I have a solution that is two layered just as your code, and is very simple when compared with your code... I'm also very happy of the code that prints a summary at the end of the job, have a look at it and enjoy the little perversions that also a clean, rationalist programming language allows you once in a while... OK, here is my code, just a word, I shortened the variable names, omitted comments etc by lazyness...

def get_poetry(f):
    d = {}
    while 1:
        l = f.readline()
        if l == '': break # end of file
        name = l.strip()
        d[name] = get_sr(f)
    return d

def get_sr(f):
    s = [] ; r = []
    while 1:
        l = f.readline()
        if l == '\n' or l == '': return s, r
        s_i, r_i = l.strip().split()
        s.append(s_i) ; r.append(r_i)

d = get_poetry(open('poetry.txt')

print '\n\n'.join(['\n'.join([
    name,
    "    syllables: "+" ".join(["%2s"%(count,) for count in sr[0]]),
    "       rhymes: "+" ".join(["%2s"%(c,) for c in sr[1]])])
                   for name, sr in d.items()])

Putting the above in a file and executing gives you

Limerick
    syllables:  8  8  5  5  8
       rhymes:  A  A  B  B  A

Sonnet
    syllables: 10 10 10 10 10 10 10 10 10 10 10 10 10 10
       rhymes:  A  B  A  B  C  D  C  D  E  F  E  F  G  G

Quintain (English)
    syllables:  0  0  0  0  0
       rhymes:  A  B  A  B  B

Rondeau
    syllables:  8  8  8  8  8  8  8  8  4  8  8  8  8  8  4
       rhymes:  A  A  B  B  A  A  A  B  C  A  A  B  B  A  C

Haiku
    syllables:  5  7  5
       rhymes:  *  *  *
gboffi
  • 22,939
  • 8
  • 54
  • 85