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