Compsci 6/101, Spring 2011, Lab 3

Turn in this page for your group

There are two parts to this lab:

  1. Designing a program that prints verses from a song and rewriting it to emphasize the power of abstraction.

  2. Practice with random numbers, lists, and strings.

Song Programs

Extending the program here that prints songs will help you practice with organizing functions into working programs. You'll also learn about abstractions which are hard to define, but whose understanding-of and practice-with are part of learning about computer science.

Cyndi Lauper narrating
the Old Lady and the Fly

There was an old woman who swallowed a fly,
I don't know why she swallowed the fly,
Perhaps she'll die.

There was an old woman who swallowed a spider,
That wriggled and jiggled and tickled inside her,
She swallowed the spider to catch the fly,
I don't know why she swallowed the fly,
Perhaps she'll die.

There was an old woman who swallowed a bird,
How absurd! to swallow a bird,
She swallowed the bird to catch the spider,
That wriggled and jiggled and tickled inside her,
She swallowed the spider to catch the fly,
I don't know why she swallowed the fly,
Perhaps she'll die.

I know an old lady who swallowed a cat,
Imagine that, to swallow a cat!
She swallowed the cat to catch the bird,
She swallowed the bird to catch the spider,
That wriggled and jiggled and tickled inside her,
She swallowed the spider to catch the fly,
I don't know why she swallowed the fly,
I guess she'll die.

There are more verses to the song, you can find them here: http://www.poppyfields.net/poppy/songs/oldwoman.html. The first two verses of the song are slightly different in the pattern they use, all the other verses have the same pattern.

You should snarf the code to the lab ( OldWoman.py), you can also find it in the code directory here. The third verse of the song is generated by the function third and the helper functions it calls: bird and birdverse:

def bird():
    a1 = swallowed("bird")
    a2 = "How absurd to swallow a bird"
    return a1+"\n"+a2

def birdverse():
    a1 = catch("bird","spider")
    a2 = spiderverse()
    return a1+"\n"+a2

def third():
    a1 = bird()
    a2 = birdverse()
    return a1+"\n"+a2
The fourth verse isn't shown in the output above, but it is part of the program you'll run and it is generated by the three functions below:
def cat():
    a1 = swallowed("cat")
    a2 = "Imagine that to swallow a cat"
    return a1+"\n"+a2

def catverse():
    a1 = catch("cat","bird")
    a2 = birdverse()
    return a1+"\n"+a2

def fourth():
    a1 = cat()
    a2 = catverse()
    return a1+"\n"+a2
First you'll answer a few questions about the pattern in the three functions that print the third and fourth verses. Then you'll rewrite these functions to make them better/more abstract. Then you'll write a new verse, then you'll work to really rewrite the program to make it much smaller and more amenable to change and extension.

We'll work to create the least amount of tedium, you may find some in this exercise -- but it's for a greater good and understanding of the power of computational abstraction.


    Song Part I

    Answer these questions on the handin sheet.

  1. Briefly, what are the differences between functions third and fourth? What are the similarities?

  2. What information do you need to write the functions to print a fifth (or any other) verse?

  3. The functions cat and bird are similar, what are the differences in the functions?

    Song Part II

    In Python you can nest related, or helper functions inside another function -- this isn't always a good idea, but it is sometimes. For example, the three functions to create the third verse: bird, birdverse, third could be written as follows.
    def birdverse():
        a1 = catch("bird","spider")
        a2 = spiderverse()
        return a1+"\n"+a2
    
    def third():
    
        def bird():
            a1 = swallowed("bird")
            a2 = "How absurd to swallow a bird"
            return a1+"\n"+a2
    
        a1 = bird()
        a2 = birdverse()
        return a1+"\n"+a2
    
    Note that the function bird is now nested inside the function third and called from the function third. Since this is the only place bird is called, it's a good idea to nest it inside third since there's no need to call it from anywhere else in the program.

  4. Give a reason why the function birdverse cannot be moved inside the function third. If you cut/copy/paste it inside third, what does Eclipse tell you?

  5. Variables with the names a1 and a2 appear in both bird and third -- provide a plausible explanation of why this is ok and doesn't cause confusion in the Python interpreter.

  6. Create a new version of the function fourth using the the nesting technique in your working version of the program. What are the last three lines of the function you wrote?
  7. Modify the Python code by adding appropriate functions in the same style as those you're given to print the new verse below in addition to all the verses that are already printed. What are the names of the new functions you wrote? What are the last three lines of the function you wrote that contains a nested function?

    I know an old lady who swallowed a dog,
    My, what a hog, to swallow a dog!
    She swallowed the dog to catch the cat,
    She swallowed the cat to catch the bird,
    She swallowed the bird to catch the spider,
    That wriggled and jiggled and tickled inside her,
    She swallowed the spider to catch the fly,
    I don't know why she swallowed the fly,
    I guess she'll die.
    

    Song Part III

    This is a challenge section to do after completing the Random Steps section. First do that.

Random Steps

Random walks are extremely important techniques in some scientific simulation and optimization areas, are interesting to some branches of mathematics, and can be both interesting and amusing to watch and analyze.

The function RandomWalk.py shows by a sequence of printed X's a simulation of a bug or person walking left or right with equal probability starting roughly in the middle of the screen. Here's the first part of one run:

                 X
                  X
                   X
                    X
                     X
                    X
                     X
                    X
                     X
                      X
                       X
                      X

(answer on handin pages)

  1. Modify the program so that instead of taking 50 steps, the bug takes 100 steps. Then 1000 steps. Does the bug get to the left in these runs (e.g., is the bug's position zero)?

  2. What happens if the variable location is initialized to 0 and to 80 when the program is run? Why?

  3. Modify the program to make it twice as likely that the bug moves right than left. What did you to to make this happen? What happens to the visualization of the random walk?

  4. For this question you'll write some functions to analyze the visualization of the walk that's printed, e.g., the string visual in the function main. Here's the function you should write in RandomWalk.py -- then call this function appropriately from main:
    def analyze(visual):
        steps = visual.split("\n")
        print "number of steps taken: ",len(steps)
    
    1. What is the purpose of the call to split and the variable steps in the code above?

    2. What is the call to analyze from main?

    3. In words, how would you find the location farthest to the right in the bug's walk as part of the analysis? Use words, not code.

  5. Modify the function analyze so that it prints the bug's location that's farthest to the right using the code below. Then answer the questions about the code.
    def analyze(visual):
        steps = visual.split("\n")
        print "number of steps taken: ",len(steps)
        locs = [elt.find("X") for elt in steps]
        print "farthest right: ",max(locs)
    
    
    1. What does the string method find do?

    2. In words, what are the contents of list locs in the code above?

    3. What does the function max do?

    4. How would you find the farthest left location? What do you expect it to be?

    5. The function sum returns the sum of all numbers in a list. Find the average value of the bug's location during a walk by writing Python code. What's the code? What's the average value in a random walk of 1000 steps?

Song III

To do if time and as a challenge to increase your understanding and application of abstraction.

(answer on handin pages)

In Part II you nested bird inside of third, nested cat inside of fourth, and nested dog inside of fifth. The verse-functions: third, fourth, fifth are very similar, but each is different because of the function that's not nested, e.g., birdverse and dogverse and because of the names and characteristics of the animals. In this section you'll find a way to avoid the duplication by leveraging abstraction using functions that return functions instead of strings.

Here's the new function that will take the place of third, fourth, fifth and other verse-fuctions, including the helper functions each one calls.

Here's the code -- you'll be asked questions about it, but you'll need to type it into Eclipse, or to use the module MetaOldWoman.py that was snarfed.

def make_verse(animal, characteristic, eaten, previous):
    def opening():
        a1 = swallowed(animal)
        a2 = characteristic
        return a1 + "\n" + a2
    def chorus():
        a1 = catch(animal,eaten)
        a2 = previous()
        return a1 + "\n" + a2
    
    return (opening,chorus)

Here's a main that uses this function:
def main():
    print first()
    print
    print second()
    print
    bird,birdverse = make_verse("bird","How absurd to swallow a bird","spider",spiderverse)
    cat,catverse = make_verse("cat","Imagine that to swallow a cat","bird",birdverse)

    print bird()
    print birdverse()
    print
    print cat()
    print catverse()
    print

  1. The function make_verse returns two things, both are functions. What are the clues in the return statement and in the function call that two things are returned?

  2. What are the clues in the return statement and in how the returned values are used in main that the return values are functions and not strings?

  3. What is the call to make_verse that will result in generating the verse about the woman eating a dog? How will you use the returned values to print the verse?

  4. The final step in this abstraction is to use lists to avoid the redundancy in the names used to store the returned functions in the many calls to make_verse. Here's a new version of main that you should try to finish and about which you'll answer questions.
    def metamain():
        print first()
        print
        print second()
        print
        animals =  ["spider","bird","cat","dog","goat"]
        exclaims = ["",
                    "How absurd to swallow a bird",
                    "Imagine that to swallow a cat",
                    "What a hog to swallow a dog",
                    "Just opened her throat and swallowed a goat"]
        
        lastverse = spiderverse
        for i in range(1,len(animals)):
            start,verse = make_verse(animals[i],...
            print start()
            print verse()
            print
            lastverse=verse
    
    
    
    1. There are three parameters to add in the function call to make_verse. Add them so the program runs using this loop. What did you add?

    2. What is the purpose of the variable lastverse? What type of value does it hold?

    3. How would you add a verse about a cow about which I don't know how she swallowed a cow?

    4. Use words to describe how to add a new verse in the code above.