In Class: Week 7


Create a week07 subdirectory in your cs21/class directory, and from within your week07 subdirectory copy over some python files from my public directory:

    $ cd 
    $ cd cs21/class
    $ pwd
      /home/your_user_name/cs21/class
    $ mkdir week07        
    $ cd week07
    $ pwd
      /home/your_user_name/cs21/class/week07
    $ cp ~newhall/public/cs21/week07/* .
    $ ls
	
After we work on in-class assignments together, you can copy over the code I wrote in class:
	$ cp ~newhall/public/cs21/week07/tia* .
	
My solutions are in files with the prefix tias_.
We are going to do some of the following together in class:
  1. strings and lists as objects:

    On the class schedule for week6 is a link to information about using strings and lists as objects, and about using the random library.

    We have talked about lists and strings as being objects and how to see a listing of the methods each provide. In the python interpreter, you can get information about list and str class method functions by calling help(class_name):

      $ python
      >>>  help(str)
      >>>  help(list)
    	

    Let's try out the split and join methods of the str and list classes. These will allow us to create a list of substrings from a string and to create a string from a list of strings.

  2. File I/O:

    open filetest.py in vim. We will look at this code together and try running it to see if we understand what it is doing.

    Rules for using a file:

    1. open a file (open returns a new file object associated with the open file). It takes the file name and the mode in which to open the file as parameters.
      		infile = open("foo.txt", "r")
      		
    2. call method functions on the file object to read or write characters from a file.
      		line = infile.readline()
      		
    3. close the file.
      		infile.close()
      		
    You can run help(file) in the python interpreter to see the method functions associated with a file object. We will likely only use readline and readlines to read in data from a file.

Top Down Design

Top Down Design is a problem solving technique where:
  1. Start with General Description of Problem
  2. Break it into several high-level steps
  3. Iteratively break the steps into smaller steps until you have steps that are easy to solve
Top-down design is a lot like creating a paper outline for a large paper where you start with the main sections, and iteratively refine each part with more and more detail until you are ready to start writing the paper.

When you use top-down design, your resulting program's structure should closely match that of the steps: the main function should have calls to a few high-level functions, usually one for each high-level step; high-level functions have calls to functions that implement sub-steps of the high-level step; and so on.

Program functions also come from recognizing part of the code (or recongizing steps in the algorithm) where that are similar to other parts and generalizing that functionality into a function.

Iterative Refinement

When writing a large program, programmers use Iterative Refinement: do some top-down design, write function stubs for this part of code and maybe some implementation, then test. Iteratively, add more functions, and perhaps refine some of the steps using Top-Down design, and test, and so on. The idea is to write some code, test it, then write a little more code, and test it before writing even more. Usually, I write a function, then test it, write another function, test it, ... This way if I'm careful about testing, I know that if there is a bug in my program it is with the new code I've just added.

Function Prototyping

We often use prototyping to just put in function stubs so we can test the whole program's flow of control without having to have a full implementation. For example, here is a stub for a function to compute square root (it doesn't actually do anything but print out a message with the parameter value and return some bogus value, but I can call it from other parts of my program):
def squareRoot(num):
  """
  This function computes the square root of a number.
    num: the number 
    returns: the square root of num
  """

  print "inside squareRoot num is", num

  # TODO: implement this function

  return 1

Let's try it out...

We are going to try applying Top-Down Design and Iterative Refinement to write a program that computes the winning percentage of the game of Craps by simulating some number of games and keeping track of the number won.

The game of Craps is played as follows:

A player rolls a pair of six-sided dice.

If the initial roll is 2, 3, or 12, the player loses.

If the initial roll is 7 or 11, the player wins.

Any other initial roll causes the player to "roll for points". This means that the player keeps rolling either until s/he re-rolls the initial value (a win) or rolls a 7 (a loss).

Program to write

Write a program to simulate multiple games of craps and estimate the probability that the player wins.

First Step of Top Down Design:

When we think about solving this problem, we apply top-down design to first break it down into a few high-level steps:
  1. Input: Get the number of games to play (better be a positive int value)
  2. Compute: Play number of games of craps
  3. Output: print out the number of games won and the winning percentage

At this point we could start writing the code for the main function, its code will look like calls to functions, each one implementing one of these high-level steps. We will add function stubs for the functions implementing these high-level steps to test our our main program's control flow.

Let's think about input and return values for functions we would write for these steps ((1) getPositiveInt (2) playGamesOfCraps (3) we likely don't need a function for this step (it consists of just one python instruction). We will just add the code to do this step directly to main.

Let's open craps.py, which contains code for this first step and test our program's control flow.

Next we will refine step (2) Play some number of games of craps, into sub-steps, and so on.

As we write individual functions, we can test them out independently of how they will be called in the final program by making test calls to them from main (or another function) passing in different test values and seeing if they return the correct values and do the right thing. Once we have tested them for correctness, we will remove these test calls.