Week 8: More top-down design

Lectures:

You must be logged on to your swarthmore.edu account for the youtube lecture videos to be viewable

Changes to the course

  • We have dropped one quiz and have shifted the remaining quizzes to be one week later

    • Quiz 4 will be April 3

    • Quiz 5 will be April 17

  • We are still figuring out how the quizzes and the final exam will be administered

  • Attendance will no longer count as part of participation

  • Labs will continue to be made available on Sunday afternoons and be due on Saturday evenings, however, we understand that you may have various technical difficulties. Please email me if you need some additional time to complete the lab.

Remote learning logistics

  • Grading feedback for quizzes will be given through Gradescope. You should have gotten an email from Gradescope to login and create an account. I have made your Quiz 3 score and comments available here.

  • Grading feedback for labs will be given through emails.

  • Lectures will happen at the normal times on Zoom: MWF 9:30-10:20 (EST)

  • Lectures will be recorded and available on youtube for students who are unable to attend live (see links above)

  • Labs, Ninja sessions, and Office hours will all be coordinated through Slack

  • Slack is integrated with Zoom, allowing us to instantly set up one-on-one meetings with you where you can share your screen with us to easily get help

  • If your internet access is spotty, you can also do handin21 to make your latest code available for us to run and we can help you through email

  • My office hours will be at the normal times: TR 1:30-3:30 (EST)

Monday: Review top-down design

Do an update21 to get the inclass examples for this week * The file design-flashcards.py contains a top-down design * The file flashcards.py contains the final implementation

Wednesday: TDD example Math quiz

Do an update21 to get the files output-mathquiz.txt and design-mathquiz.py. Today we will work on another top-down design example. We want to generate a math quiz for young students that will help them practice addition and subtraction.

Sample output

Here’s an example of the output we would like the program to produce:

======================================
             Welcome to Math Quiz!
======================================

How many questions would you like? 5

1. What is 9 + 7 ?
Your answer: 16
Great job!

2. What is 8 - 1 ?
Your answer: 7
Well done!

3. What is 10 + 5 ?
Your answer: 14
Sorry, the answer is 15

4. What is 4 + 4 ?
Your answer: 8
You know your stuff.

5. What is 8 + 6 ?
Your answer: 14
You know your stuff.

You got 4 out of 5 correct.

We would like the program to generate different random problems every time we run it. We learned about the random library before break, but let’s review some of the things we can do.

Review random library

Open up the python3 interpreter and import the random library (as shown below). This library contains lots of useful functions, but we primarily use three of them: randrange to generate random integers, choice to choose a random item from a list, and random to generate a random float between 0 and 1.

oil[w08]$ python3
Python 3.6.9 (default, Nov  7 2019, 10:44:02)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from random import *
>>> randrange(1,100)
55
>>> randrange(1,100)
90
>>> randrange(1,100)
30
>>> randrange(1,100)
24
>>> choice(['a', 'b', 'c'])
'b'
>>> choice(['a', 'b', 'c'])
'c'
>>> choice(['a', 'b', 'c'])
'c'
>>> choice(['a', 'b', 'c'])
'a'
>>> random()
0.07312210495728821
>>> random()
0.3000957770787235
>>> random()
0.6630582612298888

OK, now that we remember how the random library works, let’s get back to our top-down design.

Focus on major steps

The goal of top-down design is to think through the major steps we need to do in order to solve a problem without getting bogged down too much in the details.

In addition, we want our main program to be short and clear. Recall that there are always two audiences for a program—​the computer and other humans. The computer only cares whether the program is syntactically correct, while other humans want to be able to understand how the program works. This is why we try to choose clear names for variables and functions, and also why we try to break the problem up into smaller pieces.

In today’s exercise we will focus on the main program, and not worry about creating any stubbed out functions yet. Take 5 minutes and sketch out how you think the main program will work. Any time you feel like you need a function, just invent one. Go do this now and come back when you’re done.

How many functions did you invent?

I created 5, but at a minimum you probably need at least 3. Here is a summary of the functions I invented:

  • introduction() prints the welcome message

  • generateQuestion(num) generates a random arithmetic question, prints it, and returns the correct answer

  • checkStudent(answer) gets the student’s response, checks it against the answer, and returns True if correct or False if not

  • generatePraise() prints different messages when the student gets the right answer

  • reportResults(numCorrect, numQuestions) prints a summary message about the results

Here’s my rough draft for the main program:

def main():
    introduction()
    numQuestions = int(input("How many questions? "))
    numCorrect = 0
    for i in range(numQuestions):
        answer = generateQuestion(i+1)
        if checkStudent(answer):
            numCorrect = numCorrect + 1
            generatePraise()
        else:
            print("Sorry, the answer is", answer)
    reportResults(numCorrect, numQuestions)

Complete top-down design

Now you can stub out every function that you created in your draft main. Each stub should include a triple-quoted comment describing the purpose of the function, the parameters it takes (if any), and the return value (if any). It should print a message that the function was called. If a function returns something, it should return a dummy value of the correct type.

Here’s is my completed top-down design:

def main():
    introduction()
    numQuestions = int(input("How many questions? "))
    numCorrect = 0
    for i in range(numQuestions):
        answer = generateQuestion(i+1)
        if checkStudent(answer):
            numCorrect = numCorrect + 1
            generatePraise()
        else:
            print("Sorry, the answer is", answer)
    reportResults(numCorrect, numQuestions)

def introduction():
    """
    Purpose: Prints a welcome message.
    Returns nothing
    """
    print("Calling introduction...")

def generateQuestion(qNum):
    """
    Purpose: Generates a random addition or subtraction problem. Prints
    the problem.
    Parameter: an integer representing the current question number
    Returns: integer representing the correct answer
    """
    print("Calling generateQuestion...")
    return 0

def generatePraise():
    """
    Purpose: Generates different praise messages
    Returns nothing
    """
    print("Calling generatePraise...")

def checkStudent(answer):
    """
    Purpose: Gets student's response and compares to correct answer.
    Parameter: an integer answer to the problem
    Returns: True if correct, False otherwise
    """
    print("Calling checkStudent...")
    return True

def reportResults(numCorrect, numQuestions):
    """
    Purpose: Report how student did
    Parameters: two integers representuing number correct and number of
    questions
    Returns nothing
    """
    print("Calling reportResults...")

main()

Your top-down design must be syntactically correct and executable. Here’s a sample run of my design:

Calling introduction...
How many questions? 3
Calling generateQuestion...
Calling checkStudent...
Calling generatePraise...
Calling generateQuestion...
Calling checkStudent...
Calling generatePraise...
Calling generateQuestion...
Calling checkStudent...
Calling generatePraise...
Calling reportResults...

Notice that the design correctly generates a question, checks the answer, and generates praise three times before reporting the results.

Bottom-up implementation

Once your top-down design is complete, you can begin implementing the functions in any order you’d like. I am going to work on the generateQuestion function first. I will use randrange from the random library to help me come up with different questions each time.

def generateQuestion(qNum):
    """
    Purpose: Generates a random addition or subtraction problem. Prints
    the problem.
    Parameter: an integer representing the current question number
    Returns: integer representing the correct answer
    """
    num1 = randrange(0,11)
    num2 = randrange(0,11)
    option = randrange(2)
    if option == 0: # addition problem
        answer = num1 + num2
        print("%d. What is %d + %d ?" % (qNum, num1, num2))
    else: # subtraction problem
        answer = num1 - num2
        print("%d. What is %d - %d ?" % (qNum, num1, num2))
    return answer

It is essential that you test each function as you add it! This is called incremental testing. Ensure that each new function is working before building the next one.

Incremental testing

Here’s the output that my partially completed program will produce now:

Calling introduction...
How many questions? 3
1. What is 7 - 4 ?
Calling checkStudent...
Calling generatePraise...
2. What is 0 + 4 ?
Calling checkStudent...
Calling generatePraise...
3. What is 7 - 5 ?
Calling checkStudent...
Calling generatePraise...
Calling reportResults...

If you’d like to, you can complete the bottom-up implementation of this program.

Friday: Reading/Writing files

Before spring break we talked about how to read from a file:

Now we will learn about how to write to a file.

Basics of writing to a file

  • Open the filename that you want to write to and designate that you want to write to it using the "w" mode.

file = open("test.txt", "w")
  • Use write to add to the file. Note that you must explicitly add newline characters whenever you want to move to the next line in the file.

file.write("This is a test!\n")
file.write("Here's some more...\n")
file.write("Bye\n")
  • Close the file.

file.close()
  • Once you’ve created a file, you can open it in atom to see its contents or you can use the cat command in the terminal to see its contents.

% cat test.txt
This is a test!
Here's some more...
Bye