1. In this question you need to fix an error in each of two function designs. First, read the data definitions, then fix the error in Problem A, and then fix the error in Problem B.
## Data Definitions
from cs103 import *
from typing import NamedTuple, List
Expense = NamedTuple('Expense', [('cost', int), # in range [0, ...)
('desc', str)])
# interp. an expense with its cost in whole Canadian dollars and its description
E1 = Expense(120, 'textbook')
E2 = Expense(4, 'coffee')
# template based on compound
def fn_for_expense(e: Expense) -> ...:
return ... (e.cost,
e.desc)
# List[Expense]
# interp. a list of expenses
L0 = []
L1 = [E1, E2]
# template based on arbitrary-sized and the reference rule
def fn_for_loe(loe: List[Expense]) -> ...:
# description of the acc
acc = ... # type: ...
for e in loe:
acc = ... (fn_for_expense(e), acc)
return acc
a. [2 marks] When the following code is run, a typecheck error is generated. The error message is shown beneath the code. Fix the error by neatly editing the code.
@typecheck
def is_expensive(e: Expense) -> bool:
"""
return True if e cost more than $100, False otherwise
"""
#return False # body of the stub
# template from Expense
return e.cost > 100
@typecheck
def num_expensive(loe: List[Expense]) -> int:
"""
return the number of expenses that cost over $100
"""
#return 1 # body of the stub
# template from List[Expense]
# acc contains the number of the expenses over $100 seen so far
acc = 0 # type: int
for e in loe:
if is_expensive(e.cost):
acc = acc + 1
return acc
start_testing()
expect(is_expensive(E1), True)
expect(is_expensive(E2), False)
expect(is_expensive(Expense(99, "shoes")), False)
expect(is_expensive(Expense(100, "gift")), False)
expect(is_expensive(Expense(101, "theatre tickets")), True)
expect(num_expensive([]), 0)
expect(num_expensive([E1, E2]), 1)
summary()
b. [2 marks] When the following code is run, a TypeError is generated. The error messages are shown beneath the code. Fix the error by neatly editing the code. (You do not need to finish the incomplete function
designs.)
@typecheck
def has_description(e: Expense, d: str) -> bool:
"""
return True if e's description is d, False otherwise
"""
return False # body of the stub
@typecheck
def match_description_only(loe: List[Expense], d: str) -> List[Expense]:
"""
return a list of expenses from loe that have the description d
"""
return [] # body of the stub
start_testing()
expect(has_description(E1, 'textbook'), True)
expect(has_description(E1, 'text book'), False)
expect(has_description(E2, 'textbook'), False)
expect(match_description_only( [], ‘’ ), [])
expect(match_description_only([E1, E2], 'textbook'), [E1])
summary()
2. [20 marks] For each statement below, circle whether you think it is true or false and write a brief justification explaining your choice.
a. In a function design, the body of the stub returns the most likely return value.
True False
Justification: The body of the stub just has to match the return type in the signature. It doesn’t matter what value it is as long as the type matches.
b. In a function design that has a bool return type, there should be at least two tests (one that expects True and one that expects False).
True False
Justification: We should test all possible values.
c. Examples in the form. of expects are an important part of the How to Design Data Definitions recipe because they show programmers how to use data of that type.
True False
Justification: Expect calls are used in function designs. Data definitions need to have examples of what the data should look like.
d. Assume I want my program to read information from a file, filter out the data that I don’t want to plot,
and then plot the data on a line chart. The function composition given below calls the helper functions in the correct order.
filter_data(plot_line_chart(read(filename)))
True False
Justification: plot_line_chart() and filter_data() are switched around.
e. When a function design includes an accumulator, the accumulator type is always the same as the return type.
True False
Justification: The accumulator stores information that we will need in the final answer but there is no
guarantee that it holds the actual answer to the question. For example, if my question took in a list and returned a bool indicating whether the list had more than five items that met a certain criteria, the
accumulator could be an int counting the number of items that met the criteria but the actual return value of the function is a bool.
f. When designing a function that takes an enumeration as input, you must copy the enumeration’s template and use it without changing the if/elif/else structure.
True False
Justification: We shouldn’t collapse the if/elif/else structure because it lowers readability.
g. Functions cannot contain more than one return statement.
True False
Justification: You can have as many return statements as you want but when the function executes, the first return statement it encounters will be where the function ends.
h. If the information that you are trying to represent are two distinct values that are opposites of each other, you should use an enum.
True False
Justification: A bool is a better representation.
i. A test failure means that your code has syntax errors and does not run.
True False
Justification: It means that your expected value and actual value did not match up, not that your code cannot run.
j. The template you should use in a function design depends on the type of its parameter.
True False
Justification: The type of parameter determines which data template we need to copy over.
3. [19 marks] You are building an online ordering app for Mercante, a pizza restaurant, and need to design data
definitions for their menu items. You have their menu items in a .csv file. Here are the first few rows of the .csv file.
Menu Item
|
Category
|
Cost
|
Vegetarian
|
margherita pizza
|
food
|
8.95
|
Yes
|
pizza bianca
|
food
|
9.95
|
Yes
|
alla salsiccia pizza
|
food
|
9.95
|
No
|
latte
|
non-alcoholic beverage
|
4.00
|
Yes
|
house red wine
|
alcohol
|
8.50
|
Yes
|
Mercante only has three categories of menu items that it sells (food, non-alcoholic beverage, alcohol).
Design data definitions to represent all of the information as data in your program.
Marking scheme
Enum Data Definition (Category)
• 1 for a meaningful name
• 1 for data type definition
• 1 for interpretation
• 1 note that examples are redundant
• 1 correct template
• 1 correct template rules
Compound Data Definition (MenuItem)
• 1 for a meaningful name
• 1 for data type definition
• 1 necessary fields present
• 1 fields have the right type
• 1 for interpretation - must include units for amount
• 1 for at least one correct example
• 1 correct template
• 1 correct template rules
List Data Definition (List[MenuItem])
• 1 for data type definition and interpretation
• 2 for at least two correct examples (one empty, one non-empty)
• 1 for correct template
• 1 for correct template rules
4. [9 marks] Design a data definition to represent the amount of snow at UBC on a particular day. Sometimes the amount of snow is unknown (e.g. the sensor wasn’t working that day).
Snow = Optional[float] # in range[0, ...)
# interp. the amount of snow on a particular day, measured in mm, or None
# if the amount is unknown
S1 = 4.5
S2 = None
# template based on Optional
def fn_for_snow(s: Snow) -> ...:
if s is None:
return ...
else:
return ... (s)
Marking scheme
Optional Data Definition (Snow)
• 1 for a meaningful name
• 1 for Optional data type
• 1 for stating the range of the int or float
• 2 for interpretation - must include units and a description of what None represents
• 2 for at least two correct example (one must be the normal case and one must be the None case)
• 1 correct template
• 1 correct template rules
5. [2 marks] Give one concrete example of how the planning steps of the How to Design Analysis Programs recipe make building the program easier.
• It forces you to take a look at the data that you have and the question you want to answer to determine what information you are lacking before you start. That way, you can plan accordingly to figure out a way to get all the pieces of information you need.
• Writing down what you expect first will ensure that your program is actually doing what you want.
When you program too much, sometimes you forget what you are trying to do. Having the planning steps first helps ensure that you have a clearly defined goal.
6. [7 marks] You want to go on vacation after your last exam and have been searching for airfares. You’ve been keeping the information in a .csv file and want to read it in to a program for analysis. Here are some of the airfares that you’ve found.
Departure
date
|
Departure
time
|
Departure
City
|
Arrival
Date
|
Arrival Time
|
Arrival
city
|
Price
|
20-Dec-17
|
14:53
|
YVR
|
21-Dec-17
|
14:00
|
CUN
|
339
|
20-Dec-17
|
22:00
|
YVR
|
21-Dec-17
|
16:59
|
CUN
|
473
|
20-Dec-17
|
8:05
|
YVR
|
21-Dec-17
|
17:22
|
HUX
|
516
|
This information is available in csv format in the file airfares.csv. Here are data definitions that can be used to represent the relevant parts of this information as data in your program. THE PROBLEM STATEMENT IS ON THE NEXT PAGE.
from typing import NamedTuple, List
from cs103 import *
import csv
Airfare = NamedTuple('Airfare', [('dept', str),
('arr', str),
('cost', float)]) # in range [0, ...)
# interp. an airfare with its departure city, arrival city, and cost in CAD
A1 = Airfare('YVR', 'CUN', 339.0)
A2 = Airfare('YVR', 'CUN', 473.0)
A3 = Airfare('YVR', 'HUX', 516.0)
# template based on compound
def fn_for_airfare(a: Airfare) -> ...:
return ... (a.dept,
a.arr,
a.cost)
# List[Airfare]
# interp. a list of airfares
L0 = []
L1 = [A1, A3, A3]
# template based on arbitrary-sized and the reference rule
def fn_for_loa(loa: List[Airfare]) -> ...:
# description of the acc
acc = ... # type: ...
for a in loa:
acc = ... (fn_for_airfare(a), acc)
return acc
Continuing Problem 6: Design a read function to read this information from a file and store it as data in your program. We have provided the read template and suggest that you neatly edit it rather than re-writing your read function. Assume that the information is in a file named “airfares.csv”. If you need to create another information file, you can draw a table that shows the information (as above) and label it with the filename.
def read(filename: str) -> List[Consumed]:
"""
reads information from the specified file and returns ...
"""
# loc contains the result so far
loc = [] # type: List[Consumed]
with open(filename) as csvfile:
reader = csv.reader(csvfile)
next(reader) # skip header line
for row in reader:
# you may not need to store all the rows, and you may need
# to convert some of the strings to other types
c = Consumed(row[0], ... ,row[n])
loc.append(c)
return loc
Marking scheme
• 1 – update return type
• 1 – update purpose
• 1 – update acc type
• 2 – correctly update line that creates the Airfare (including call to parse_float)
o 1 mark for correctly using parse_float instead of parse_int
o 1 mark for using the correct column indicies
• 2 – at least two correct examples
7. [13 marks] Perhaps you’ve been searching for a flight for your vacation for quite some time and have been
tracking the cost of a particular flight over time. Every day you’ve searched for the best price and have saved
that information in a .csv file that uses the same format as “airfares.csv”. Design a function that takes a list of airfares and plots the airfares over time on a line chart.
You can assume that the airfares are in the order in which they should be plotted and that your x-axis values can just be the record numbers (1, 2, 3, etc.). You can assume that the function get_range (below) works correctly. (You must use the data definitions from Problem 6.)
def get_range(n: int) -> List[int]:
"""
returns a list of integers from 1 to n
"""
# description of the accumulator
acc = [] # type: List[int]
count = 1
while count <= n:
acc.append(count)
count = count + 1
return acc
start_testing()
expect(get_range(3), [1, 2, 3])
expect(get_range(4), [1, 2, 3, 4])
summary()
Marking scheme
Helper function
2 – signature
1 – purpose
1 – based on correct template
1 – at least one correct example
1 – correctness
2 signature
1 purpose
1 correct title, x-axis label and y-axis label
2 correct arguments pass to pyplot.plot
1 at least 1 correct example