[2021 Spring] CS61A 学习笔记 Homework 5: Object-Oriented Programming, Linked Lists, Iterators and Generators
作业说明: https://inst.eecs.berkeley.edu/~cs61a/sp21/hw/hw05/
目录
Q1: 自动售货机
根据doctests添加类的属性和方法。创建类时传递商品名称和价格,库存和已投币金额初始化为0。
注意:
- 不要使用print,使用return才会返回字符串,格式转化可使用 f''。
- 成功售卖后,库存减一、 金额置零。
class VendingMachine:
"""A vending machine that vends some product for some price.
>>> v = VendingMachine('candy', 10)
>>> v.vend()
'Inventory empty. Restocking required.'
>>> v.add_funds(15)
'Inventory empty. Restocking required. 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)
'Inventory empty. Restocking required. 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.'
"""
"*** YOUR CODE HERE ***"
代码
class VendingMachine:
def __init__(self, product, price):
self.product = product
self.price = price
self.funds = 0
self.inventory = 0
def vend(self):
if self.inventory == 0:
return f'Inventory empty. Restocking required.'
else:
if self.funds < self.price:
return f'You must add ${self.price - self.funds} more funds.'
elif self.funds == self.price:
self.funds = 0
self.inventory -= 1
return f'Here is your {self.product}.'
else:
fund = self.funds
self.funds = 0
self.inventory -= 1
return f'Here is your {self.product} and ${fund - self.price} change.'
def add_funds(self, balance):
self.funds += balance
if self.inventory == 0:
fund = self.funds
self.funds = 0
return f'Inventory empty. Restocking required. Here is your ${fund}.'
else:
return f'Current balance: ${self.funds}'
def restock(self, stock):
self.inventory += stock
return f'Current {self.product} stock: {self.inventory}'
Q2: Store Digits
循环获得数字n的最后一位,添加到当前链表的头部。
def store_digits(n):
"""Stores the digits of a positive number n in a linked list.
>>> s = store_digits(1)
>>> s
Link(1)
>>> store_digits(2345)
Link(2, Link(3, Link(4, Link(5))))
>>> store_digits(876)
Link(8, Link(7, Link(6)))
>>> # a check for restricted functions
>>> import inspect, re
>>> cleaned = re.sub(r"#.*\\n", '', re.sub(r'"{3}[\s\S]*?"{3}', '', inspect.getsource(store_digits)))
>>> print("Do not use str or reversed!") if any([r in cleaned for r in ["str", "reversed"]]) else None
"""
"*** YOUR CODE HERE ***"
代码
def store_digits(n):
res = Link.empty
while n > 0:
rem = n % 10
res = Link(rem, res)
n //= 10
return res
Q3: Yield Paths
- t.label == value时,返回[value];
- t.label != value时,for循环遍历树枝,使用path_yielder递归查找;只有当1中生成[value]时,将上一层的t.label与当前path结合。
- 对于t1 = Tree(1, [Tree(2, [Tree(3), Tree(4, [Tree(6)]), Tree(5)]), Tree(5)]),next(path_yielder(t1, 6))调用过程:t.label == 6,生成器返回 [6],上一层数t.label =4,for循环内生成[4, 6],依次往上直到根节点。
def path_yielder(t, value):
"""Yields all possible paths from the root of t to a node with the label value
as a list.
>>> t1 = Tree(1, [Tree(2, [Tree(3), Tree(4, [Tree(6)]), Tree(5)]), Tree(5)])
>>> print(t1)
1
2
3
4
6
5
5
>>> next(path_yielder(t1, 6))
[1, 2, 4, 6]
>>> path_to_5 = path_yielder(t1, 5)
>>> sorted(list(path_to_5))
[[1, 2, 5], [1, 5]]
>>> t2 = Tree(0, [Tree(2, [t1])])
>>> print(t2)
0
2
1
2
3
4
6
5
5
>>> path_to_2 = path_yielder(t2, 2)
>>> sorted(list(path_to_2))
[[0, 2], [0, 2, 1, 2]]
"""
"*** YOUR CODE HERE ***"
for _______________ in _________________:
for _______________ in _________________:
"*** YOUR CODE HERE ***"
代码
def path_yielder(t, value):
"*** YOUR CODE HERE ***"
if t.label == value:
yield [value]
for b in t.branches:
for path in path_yielder(b, value):
"*** YOUR CODE HERE ***"
yield [t.label] + path
Q4: Mint 铸币厂
铸币厂类,输出所制造的硬币年份和价值。
- 每个Mint实例有一个年份戳year。update方法将Mint类的current_year参数更新到self.year。
- create方法根据Coin种类kind创建一个实例,年份为self.year(与未更新的Mint.current_year可能不同)。
- Coin的worth方法返回硬币的价值,硬币的价值等于硬币面值+(已产出年限-50),注意已产出年限不超过50时,硬币价值等于面值。
class Mint:
"""A mint creates coins by stamping on years.
The update method sets the mint's stamp to Mint.current_year.
>>> mint = Mint()
>>> mint.year
2020
>>> dime = mint.create(Dime)
>>> dime.year
2020
>>> Mint.current_year = 2100 # Time passes
>>> nickel = mint.create(Nickel)
>>> nickel.year # The mint has not updated its stamp yet
2020
>>> nickel.worth() # 5 cents + (80 - 50 years)
35
>>> mint.update() # The mint's year is updated to 2100
>>> Mint.current_year = 2175 # 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
"""
current_year = 2020
代码
class Mint:
current_year = 2020
def __init__(self):
self.update()
def create(self, kind):
"*** YOUR CODE HERE ***"
return kind(self.year)
def update(self):
"*** YOUR CODE HERE ***"
self.year = self.current_year
class Coin:
def __init__(self, year):
self.year = year
def worth(self):
"*** YOUR CODE HERE ***"
if self.year == Mint.current_year:
return self.cents
else:
return self.cents + Mint.current_year - self.year - 50
class Nickel(Coin):
cents = 5
class Dime(Coin):
cents = 10
Q5: Is BST 二叉搜索树
有效binary search tree:
- 每个节点最多有两个子节点(叶节点本身即是有效bst)
- 每个子节点都是有效bst
- 对于每个节点,左子节点的值都小于等于该节点的值
- 对于每个节点,右子节点的值都大于该节点的值
注意:当node只有一个child时,可以把child当作左右任一边。
辅助函数:bst_min和bst_max,返回左右子树的最小值和最大值。
def is_bst(t):
"""Returns True if the Tree t has the structure of a valid BST.
>>> t1 = Tree(6, [Tree(2, [Tree(1), Tree(4)]), Tree(7, [Tree(7), Tree(8)])])
>>> is_bst(t1)
True
>>> t2 = Tree(8, [Tree(2, [Tree(9), Tree(1)]), Tree(3, [Tree(6)]), Tree(5)])
>>> is_bst(t2)
False
>>> t3 = Tree(6, [Tree(2, [Tree(4), Tree(1)]), Tree(7, [Tree(7), Tree(8)])])
>>> is_bst(t3)
False
>>> t4 = Tree(1, [Tree(2, [Tree(3, [Tree(4)])])])
>>> is_bst(t4)
True
>>> t5 = Tree(1, [Tree(0, [Tree(-1, [Tree(-2)])])])
>>> is_bst(t5)
True
>>> t6 = Tree(1, [Tree(4, [Tree(2, [Tree(3)])])])
>>> is_bst(t6)
True
>>> t7 = Tree(2, [Tree(1, [Tree(5)]), Tree(4)])
>>> is_bst(t7)
False
"""
"*** YOUR CODE HERE ***"
代码
def is_bst(t):
"*** YOUR CODE HERE ***"
def bst_min(t):
if t.is_leaf(): return t.label
return min(t.label, bst_min(t.branches[0]))
def bst_max(t):
if t.is_leaf(): return t.label
return max(t.label, bst_max(t.branches[-1]))
if t.is_leaf(): return True
if len(t.branches) == 1:
child = t.branches[0]
return is_bst(child) and \
(bst_max(child) <= t.label or bst_min(child) > t.label)
elif len(t.branches) == 2:
left_child, right_child = t.branches
return is_bst(left_child) and is_bst(right_child) and \
bst_max(left_child) <= t.label and bst_min(right_child) > t.label
else:
return False
Q6: Preorder
for循环遍历树枝即可
def preorder(t):
"""Return a list of the entries in this tree in the order that they
would be visited by a preorder traversal (see problem description).
>>> numbers = Tree(1, [Tree(2), Tree(3, [Tree(4), Tree(5)]), Tree(6, [Tree(7)])])
>>> preorder(numbers)
[1, 2, 3, 4, 5, 6, 7]
>>> preorder(Tree(2, [Tree(4, [Tree(6)])]))
[2, 4, 6]
"""
"*** YOUR CODE HERE ***"
代码
def preorder(t):
"*** YOUR CODE HERE ***"
if t.is_leaf():
return [t.label]
res = []
for b in t.branches:
res += preorder(b)
return [t.label] + res
Q7: Generate Preorder
注意yield from 用法
def generate_preorder(t):
"""Yield the entries in this tree in the order that they
would be visited by a preorder traversal (see problem description).
>>> numbers = Tree(1, [Tree(2), Tree(3, [Tree(4), Tree(5)]), Tree(6, [Tree(7)])])
>>> gen = generate_preorder(numbers)
>>> next(gen)
1
>>> list(gen)
[2, 3, 4, 5, 6, 7]
"""
"*** YOUR CODE HERE ***"
代码
def generate_preorder(t):
"*** YOUR CODE HERE ***"
yield t.label
for b in t.branches:
yield from generate_preorder(b)