Lab 4: Functions, Refactoring, Testing

Goals

After the lab, you should be proficient at

  1. using and defining functions within your program
  2. refactoring your code to use functions
  3. programmatically testing functions

Objective: Review

Review the slides for today.

Objective: Set Up

Objective: Programming in Python

We'll practice writing several Python programs, each in their own text file. Name the files lab4_1.py, lab4_2.py, etc.

Your programs will be graded on correctness, efficiency, style, and how well you tested them. Make sure you adhere to the good development and testing practices we discussed in class. Your code should be readable and your output should be useful and well-formatted.

After you've developed a correct solution to each program, close reopen the IDLE "shell" by running the program again (using F5), demonstrate that the program works using several good test cases, and save the output to a file named lab4_x.out, where x is the problem number.

You can use Python Visualizer to help you see what is happening in your program. This is the visualizer used in the text book.

  1. (20) Let's start by getting you comfortable with calling functions that have been defined within the same file.

    Open lab4_1.py, which you copied during set up. Read through the functions and their documentation. Focus on the function names and their API (what is their input, what do they do, what do they return?). Then, modify the main function such that it implements what is described in its comments, e.g., draws and moves bugs, using functions defined within the program or methods from the Graphics library. (Many of your questions can be answered by reading the function's documentation, so reread the documentation. Don't worry about the code within the functions; just understand how to use the functions.)

    There will be no saved output for this program.

  2. (15 pts) Let's try turning code that you wrote before into a function--Refactoring!, so that we're focused on defining functions rather than writing new code.

    Create a new file in your lab directory named lab4_2.py. Open your lab3/lab3_5.py file for reference. Refactor your code such that the code that displays the first 20 Fibonacci numbers (and only that code) is in a function called display_fibonacci_sequence in lab4_2.py. This function should not take any parameters and does not return anything.

    Then, from the original file, copy the other code that wasn't part of the function you just created and put into a main function. Your main function will likely be quite short. Confirm that the output of the program when executed is the same as the original program from lab 3.

    Note that programmatic testing will not work for this function because the function does not return anything.

  3. (20 pts) Now let's try writing a function that takes parameters and returns something and programmatically testing that function.
    1. Open a new file (which will be named lab4_3.py).
    2. Copy your function display_fibonacci_sequence into this new file and change its name to generate_fibonacci_number. The function should take as a parameter the number of the Fibonacci sequence to generate (which must be greater than 2) and returns that Fibonacci number. Update the documentation accordingly.
    3. Write a test function that determines if your function works correctly.

      To help make the desired functionality clear, the following test case should pass:
      test.testEqual( generate_fibonacci_number(6), 5 )

    4. After you have verified that your tests work, comment out the call to your test function.
    5. Now, create a main function that prompts the user for which Fibonacci number they want and then display that Fibonacci number.

      A sample run is shown below:

      This program computes Fibonacci numbers.
              
      Which Fibonacci number are you looking for (larger than 2)? 6
      
      The Fibonacci number is 5.
      
    6. Finally, update your program to use the ordinal module (which you copied during set up).

      In the Python shell (either in the terminal or in idle), import ordinal. Then, run help(ordinal) to see what the module contains. Recall that the help function is showing you the docstrings in the given module.

      Update your code to output the ordinal number by leveraging the ordinal module. For example, the updated output for the above example would be

      This program computes Fibonacci numbers.
              
      Which Fibonacci number are you looking for (larger than 2)? 6
      
      The 6th Fibonacci number is 5.
      Your output will not have a yellow highlight like above; I'm just emphasizing the difference in the output.

    Your saved output for this program will be the user (you) running the program, not the test cases. The test cases should just print "Pass" for each one, which isn't interesting output. I will be able to see your test cases and can verify that they worked.

  4. (15) We haven't been making clear the importance of reusable functions because all of the functions we have defined have been used only within the same program.
    1. Open game.py. This file is the start of our own game module that we will use in subsequent labs.
    2. Implement the function roll_die. You may need to review the random module.

      Note that the function has a default value for the parameter sides. We have used default parameters before (e.g., the range function start is 0 and step is 1), but we haven't seen how they are defined before.

      Remove the statement pass, which is just meant to make the module be able to execute while you're still developing.

    3. Test the function using the test_roll_die function.
    4. In comments at the top of the program (below the high-level description), answer the following questions: Why can't we use test.testEqual to test this function? Why can Professor Sprenkle write a function that tests your code without knowing how your function is implemented? (Note, however, that passing the test does not guarantee that your code is correct, although passing the test means that your code is likely correct.)
    5. Implement the function roll_multiple_dice, using another function that you implemented.
    6. Uncomment the call to the function test_roll_multiple_dice and test your new function.
    7. When you're sure your game module works, save the output from executing both the test_roll_die and test_roll_multiple_dice (in one run). Save it in a file called game.out so that the output is with the relevant code in the printable lab.
    8. Open up yahtzee.py, read through the code--you'll see that it imports your roll_die function from game. Now, run it. You should see the output from rolling 5 dice. We can't do a whole lot more than that just yet, but hopefully, you could imagine all you could do with a game module. (You may need to update yahtzee to import and use roll_die instead of rollDie.)
  5. (30 pts) Overview: Write a program that calculates the surface area of a cylinder. We're breaking the code into several pieces:
    • Define a function called calculate_circle_area that takes as a parameter the radius of a circle and returns the area of the circle. (You may want to start with the code you wrote in the last lab and refactor it into a function for this problem.) Test the function in a test function.
    • Define a function called calculate_circle_circumference that takes as a parameter the radius of a circle and returns the circumference of the circle (calculated as 2 π r). Test the function in another test function.
    • Define a function that takes as parameters a cylinder's radius and height and returns the surface area of a cylinder. Name the function appropriately. To calculate the area of a cylinder, find the area of the cylinder's top or bottom circle and multiply it by two and add that to the circle's circumference, multiplied by the height of the cylinder. Test the function in yet another test function. (Note that, since you confirmed that the earlier functions worked correctly, you should feel confident using those functions to solve this problem.)
    • Finally, create a main function that prompts the user for a cylinder's radius and height and displays the surface area of the cylinder, rounded to two decimal places.

      Your final output should show multiple runs of the program, getting user input for the cylinder's dimensions, and then displaying the cylinder's area. There should be no output from calling the test function. I'll see the test cases you tried in your test functions.

Finishing up: What to turn in for this lab

You have completed several labs. I am not as explicit in the instructions anymore. Refer to the links or previous labs to remind yourself of instructions/commands.

  1. Clean up: Move graphics.py into your cs111 directory. (see mv. Note where the cs111 directory is with respect to where you're running this command from.)
    In other words, you should only have the .py files you wrote, ordinal.py, test.py, yahtzee.py, the .out files you created, and a __pycache__ directory in your directory when you print. (The latter is not printed.)
  2. Create your printable lab assignment, using the createPrintableLab command:
    createPrintableLab <labdirname>
  3. View your file using the evince command. It should only be a few pages. If it is longer than that, check if you have graphics.py in the PDF. Go back and follow the steps to move that file out of your lab directory.
  4. Print your file from Evince. If you're in the Advanced Lab, use its printer.
  5. Move graphics.py back into your lab4 directory.
  6. Submit your lab4 directory into your turnin directory.

Print your lab before class. Submit the printed and electronic version before the beginning of Friday's class.

Grading (100 pts)