Homework 4 Solutions
Solution Files
You can find solutions for all questions in hw04.py.
Required questions
Parsons Problems
To work on these problems, open the Parsons editor:
python3 parsons
Q1: Remove Odd Indices
Complete the function remove_odd_indices
, which takes in a list lst
and a boolean odd
, and returns a new list with elements removed at certain indices.
If odd
is True
, then the function should remove elements at odd indices; otherwise if odd
is False, then the function should remove
even indexed items.
Note that lists are zero-indexed; thus, the first element is at index 0, the second element is at index 1, etc.
def remove_odd_indices(lst, odd):
"""
Remove elements of lst that have odd indices.
>>> s = [1, 2, 3, 4]
>>> t = remove_odd_indices(s, True)
>>> s
[1, 2, 3, 4]
>>> t
[1, 3]
>>> l = [5, 6, 7, 8]
>>> m = remove_odd_indices(l, False)
>>> m
[6, 8]
"""
if not lst:
return lst
if odd:
return [lst[0]] + remove_odd_indices(lst[1:], not odd)
else:
return remove_odd_indices(lst[1:], not odd)
Q2: Smart Fridge
The SmartFridge
class is used by smart
refrigerators to track which items are in the fridge
and let owners know when an item has run out.
The class internally uses a dictionary to store items,
where each key is the item name and the value is the current quantity.
The add_item
method should add the given quantity
of the given item and report the current quantity.
You can assume that the use_item
method will only be called on
items that are already in the fridge, and it should use up
the given quantity of the given item. If the quantity would fall to or below zero, it should only use up to the remaining quantity, and remind the owner to buy more of that item.
Finish implementing the SmartFridge
class definition
so that its add_item
and use_item
methods work as expected.
class SmartFridge:
""""
>>> fridgey = SmartFridge()
>>> fridgey.add_item('Mayo', 1)
'I now have 1 Mayo'
>>> fridgey.add_item('Mayo', 2)
'I now have 3 Mayo'
>>> fridgey.use_item('Mayo', 2.5)
'I have 0.5 Mayo left'
>>> fridgey.use_item('Mayo', 0.5)
'Uh oh, buy more Mayo!'
"""
def __init__(self):
self.items = {}
def add_item(self, item, quantity):
if item in self.items:
self.items[item] += quantity
else:
self.items[item] = quantity
return f'I now have {self.items[item]} {item}' def use_item(self, item, quantity):
self.items[item] -= min(quantity, self.items[item])
if self.items[item] == 0:
return f'Uh oh, buy more {item}!'
return f'I have {self.items[item]} {item} left'
Code Writing Questions
Q3: Merge
Write a function merge
that takes 2 sorted lists lst1
and lst2
,
and returns a new list that contains all the elements in the two lists
in sorted order.
Note: Try to solve this question using recursion instead of iteration.
def merge(lst1, lst2):
"""Merges two sorted lists.
>>> merge([1, 3, 5], [2, 4, 6])
[1, 2, 3, 4, 5, 6]
>>> merge([], [2, 4, 6])
[2, 4, 6]
>>> merge([1, 2, 3], [])
[1, 2, 3]
>>> merge([5, 7], [2, 4, 6])
[2, 4, 5, 6, 7]
"""
# recursive
if not lst1 or not lst2:
return lst1 + lst2
elif lst1[0] < lst2[0]:
return [lst1[0]] + merge(lst1[1:], lst2)
else:
return [lst2[0]] + merge(lst1, lst2[1:])
# Iterative solution
def merge_iter(lst1, lst2):
"""Merges two sorted lists.
>>> merge_iter([1, 3, 5], [2, 4, 6])
[1, 2, 3, 4, 5, 6]
>>> merge_iter([], [2, 4, 6])
[2, 4, 6]
>>> merge_iter([1, 2, 3], [])
[1, 2, 3]
>>> merge_iter([5, 7], [2, 4, 6])
[2, 4, 5, 6, 7]
"""
new = []
while lst1 and lst2:
if lst1[0] < lst2[0]:
new += [lst1[0]]
lst1 = lst1[1:]
else:
new += [lst2[0]]
lst2 = lst2[1:]
if lst1:
return new + lst1
else:
return new + lst2
Use Ok to test your code:
python3 ok -q merge
Q4: Mint
A mint is a place where coins are made. In this question, you'll implement a Mint
class that can output a Coin
with the correct year and worth.
- Each
Mint
instance has ayear
stamp. Theupdate
method sets theyear
stamp to thepresent_year
class attribute of theMint
class. - The
create
method takes a subclass ofCoin
and returns an instance of that class stamped with themint
's year (which may be different fromMint.present_year
if it has not been updated.) - A
Coin
'sworth
method returns thecents
value of the coin plus one extra cent for each year of age beyond 50. A coin's age can be determined by subtracting the coin's year from thepresent_year
class attribute of theMint
class.
class Mint:
"""A mint creates coins by stamping on years.
The update method sets the mint's stamp to Mint.present_year.
>>> mint = Mint()
>>> mint.year
2021
>>> dime = mint.create(Dime)
>>> dime.year
2021
>>> Mint.present_year = 2101 # Time passes
>>> nickel = mint.create(Nickel)
>>> nickel.year # The mint has not updated its stamp yet
2021
>>> nickel.worth() # 5 cents + (80 - 50 years)
35
>>> mint.update() # The mint's year is updated to 2101
>>> Mint.present_year = 2176 # More time passes
>>> mint.create(Dime).worth() # 10 cents + (75 - 50 years)
35
>>> Mint().create(Dime).worth() # A new mint has the current year
10
>>> dime.worth() # 10 cents + (155 - 50 years)
115
>>> Dime.cents = 20 # Upgrade all dimes!
>>> dime.worth() # 20 cents + (155 - 50 years)
125
"""
present_year = 2021
def __init__(self):
self.update()
def create(self, coin):
return coin(self.year)
def update(self):
self.year = Mint.present_year
class Coin:
cents = None # will be provided by subclasses, but not by Coin itself
def __init__(self, year):
self.year = year
def worth(self):
return self.cents + max(0, Mint.present_year - self.year - 50)
class Nickel(Coin):
cents = 5
class Dime(Coin):
cents = 10
Use Ok to test your code:
python3 ok -q Mint
Q5: Vending Machine
In this question you'll create a vending machine that only outputs a single product and provides change when needed.
Create a class called VendingMachine
that represents a vending
machine for some product. A VendingMachine
object returns strings
describing its interactions. Remember to match exactly the strings in the
doctests -- including punctuation and spacing!
Fill in the VendingMachine
class, adding attributes and methods as
appropriate, such that its behavior matches the following doctests:
class VendingMachine:
"""A vending machine that vends some product for some price.
>>> v = VendingMachine('candy', 10)
>>> v.vend()
'Nothing left to vend. Please restock.'
>>> v.add_funds(15)
'Nothing left to vend. Please restock. Here is your $15.'
>>> v.restock(2)
'Current candy stock: 2'
>>> v.vend()
'You must add $10 more funds.'
>>> v.add_funds(7)
'Current balance: $7'
>>> v.vend()
'You must add $3 more funds.'
>>> v.add_funds(5)
'Current balance: $12'
>>> v.vend()
'Here is your candy and $2 change.'
>>> v.add_funds(10)
'Current balance: $10'
>>> v.vend()
'Here is your candy.'
>>> v.add_funds(15)
'Nothing left to vend. Please restock. Here is your $15.'
>>> w = VendingMachine('soda', 2)
>>> w.restock(3)
'Current soda stock: 3'
>>> w.restock(3)
'Current soda stock: 6'
>>> w.add_funds(2)
'Current balance: $2'
>>> w.vend()
'Here is your soda.'
"""
def __init__(self, product, price):
self.product = product
self.price = price
self.stock = 0
self.balance = 0
def restock(self, n):
self.stock += n
return f'Current {self.product} stock: {self.stock}'
def add_funds(self, n):
if self.stock == 0:
return f'Nothing left to vend. Please restock. Here is your ${n}.'
self.balance += n
return f'Current balance: ${self.balance}'
def vend(self):
if self.stock == 0:
return 'Nothing left to vend. Please restock.'
difference = self.price - self.balance
if difference > 0:
return f'You must add ${difference} more funds.'
message = f'Here is your {self.product}'
if difference != 0:
message += f' and ${-difference} change'
self.balance = 0
self.stock -= 1
return message + '.'
You may find Python's formatted string literals, or f-strings useful. A quick example:
>>> feeling = 'love' >>> course = '61A!' >>> f'I {feeling} {course}' 'I love 61A!'
Use Ok to test your code:
python3 ok -q VendingMachine
If you're curious about alternate methods of string formatting, you can also check out an older method of Python string formatting. A quick example:
>>> ten, twenty, thirty = 10, 'twenty', [30] >>> '{0} plus {1} is {2}'.format(ten, twenty, thirty) '10 plus twenty is [30]'
Reading through the doctests, it should be clear which functions we should add to ensure that the vending machine class behaves correctly.
__init__
- This can be difficult to fill out at first. Both
product
andprice
seem pretty obvious to keep around, butstock
andbalance
are quantities that are needed only after attempting other functions.
restock
- Even though
v.restock(2)
takes only one argument in the doctest, remember thatself
is bound to the object therestock
method is invoked on. Therefore, this function has two parameters. - While implementing this function, you will probably realize that you would
like to keep track of the stock somewhere. While it might be possible to set
the stock in this function as an instance attribute, it would lose whatever
the old stock was.
Therefore, the natural solution is to initialize stock in the constructor, and
then update it in
restock
.
add_funds
- This behaves very similarly to
restock
. See comments above. - Also yes, this is quite the expensive vending machine.
vend
The trickiest thing here is to make sure we handle all the cases. You may find it helpful when implementing a problem like this to keep track of all the errors we run into in the doctest.
- No stock
- Not enough balance
- Leftover balance after purchase (return change to customer)
- No leftover balance after purchase
We use some string concatenation at the end when handling case 3 and 4 to try and reduce the amount of code. This isn't necessary for correctness -- it's ok to have something like:
if difference != 0: return ... else: return ...
Of course, that would require decrementing the balance and stock beforehand.
Submit
Make sure to submit this assignment by running:
python3 ok --submit