作业7:Final Prep


解答方案

我们整理了一些旧的章节问题并编写了一些新的问题,供你在复习期末考试时使用。我们建议你在纸上写下答案以模拟考试环境,并在查看解答之前尽可能好地解决每个问题。

我们没有提供起始代码,但我们在上面提供了解答链接,欢迎你检查答案或在空白项目中练习代码。

完整程序:石头、布、剪刀

1997年,一台名为 Deep Blue 的计算机在一场国际象棋比赛中击败了世界国际象棋冠军加里·卡斯帕罗夫。2023年,IBM 终于有信心扩展其人机对战游戏曲目,并邀请你编写一个程序,让人类玩家和计算机进行石头剪刀布的对战。

🎮 游戏规则
📝 实现步骤
📋 示例运行

这是一个示例运行,用户的输入出现在 "Your move (1, 2, or 3): " 之后。记住这是一个随机程序,所以如果你运行代码,每场比赛可能会看到不同的结果。

Your move (1, 2, or 3): 1
It's a tie!
Your move (1, 2, or 3): 3
You win! Scissors cuts paper
Your move (1, 2, or 3): 2
You lose! Scissors cuts paper
Your move (1, 2, or 3): 2
It's a tie!
Your move (1, 2, or 3): 1
You win! Rock crushes scissors
You won 2 rounds!

估算 Pi

编写一个使用 Python 估算 π 值的程序!

π 是一个数学常数,来自圆的几何特性。具体来说,半径为 r 的圆将具有面积(πr²)和周长(2πr)。π 的值大约是 3.14,在这个问题中,我们将非常接近该值(我们追求的是精确到小数点后 2 位)!

估算 π 的一种好方法是使用 random 模块。想象在二维平面上绘制一个以原点为中心的 2x2 正方形,并在其中画一个半径为 1 的圆。到目前为止,我们的图形看起来像这样:

💡 思路与原理
2x2正方形与内切圆
2x2正方形与内切半径为1的圆

估计 π 的一种好方法是使用 random 模块。想象在二维平面上绘制一个以原点为中心的 2x2 正方形,并在其中画一个半径为 1 的圆:

正方形面积: 2 × 2 = 4
圆的面积: π × 1² = π
面积比: 圆/正方形 = π/4
正方形内随机点分布
正方形内随机分布的点

如果你在正方形内随机选择很多点,那么落在圆内的点的比例大约就是 π/4。

圆内外点的可视化
蓝色点在圆内,红色点在圆外
📝 实现方法

注意:你的程序会输出一个数字,而不是图形。上文中的图其实也是用 Python 生成的,你现在已经有能力做出类似的可视化啦!

图形

扩展圆圈

从画布中心的一个半径为 STARTING_RADIUS 的黑色圆圈开始,在每个时间戳绘制一个新的、更大的黑色圆圈覆盖在之前的圆圈上。每个时间戳,圆圈的半径应该增加 GROWTH_RATE。当圆圈变得与屏幕一样大(或更大)时,为白色圆圈重复此过程(从 STARTING_RADIUS 开始,增长到屏幕大小)。继续绘制这些扩展的圆圈,每次圆圈变得与屏幕一样大时,在黑色和白色圆圈之间交替(屏幕比宽度高,所以你只需要将圆圈的半径与宽度进行比较)。

这是该程序的演示:

扩展圆圈演示
📝 这是该程序的起始代码
from graphics import Canvas
import time

STARTING_RADIUS = 10
GROWTH_RATE = 5
DELAY = 1/60


def main():
    canvas = Canvas()
    width = canvas.get_width()
    height = canvas.get_height()

    # YOUR CODE HERE


if __name__ == '__main__':
    main()

魔法棒

为了模拟光标上的魔法棒,我们将绘制一个跟随用户光标的黑色矩形,并发射向上飘浮的黄色火花(圆形)。魔法棒的右下角连接到鼠标,这个魔法棒是一个尺寸为 WAND_WIDTH × WAND_HEIGHT 的黑色矩形。每个时间步,一个半径为 SPARKLE_RADIUS 的黄色圆圈应该出现在魔法棒的顶部中心,并以速度 SPARKLE_Y_VAL 向上移动。注意这个速度只是大小,所以要让火花向上移动,你应该用负速度移动它们。你应该在动画循环中编写代码,这样魔法棒和火花在每个时间步继续在屏幕上移动,延迟 DELAY 秒。火花移出屏幕是可以的;你不需要在它们移出屏幕边缘后做任何特殊处理来删除它们。你可以让魔法棒从左上角 (0, 0) 开始。

这是该程序的演示:

魔法棒跟随鼠标并发射火花
📝 这是该程序的起始代码
from graphics import Canvas
import time

WAND_WIDTH = 5
WAND_HEIGHT = 50

SPARKLE_RADIUS = 5
SPARKLE_Y_VEL = 10

DELAY = 1/60


def main():
    canvas = Canvas()
    width = canvas.get_width()
    height = canvas.get_height()

    # YOUR CODE HERE


if __name__ == '__main__':
    main()

列表

有趣的列表

编写一个函数 make_interesting(num_list),该函数接受整数列表,并根据给定列表返回一个新列表。新列表应该按照以下规则处理 num_list 中的每个值:

📋 示例调用

以下是一些 make_interesting 的示例调用及其应该返回的结果:

>>> make_interesting([-2, 33, 14, 6, -13, 9, 2])
[43, 14, 6, 19, 2]
>>> make_interesting([1, 2, 3, 4, 5])
[11, 2, 13, 4, 15]
>>> make_interesting([-10, -20, -5])
[]

折叠列表

编写一个函数 collapse(lst),该函数接受整数列表作为参数,并返回一个新列表,其中包含将每对整数替换为该对总和的结果。如果列表存储奇数个元素,则最后一个元素不折叠。

📋 示例调用

以下是一些 collapse 的示例调用:

>>> collapse([7, 2, 8, 9, 4, 13, 7, 1, 9, 10])
[9, 17, 17, 8, 19]
>>> collapse([1, 2, 3, 4, 5])
[3, 7, 5]

旋转

编写一个函数 rotate_list_right(numbers, n),返回整数列表的“右旋转”版本。每个元素向前移动 n 位,最后 n 个元素移动到开头。函数不应更改原列表,而应返回新列表。

📋 示例
>>> rotate_list_right([1, 2, 3, 4, 5], 2)
[4, 5, 1, 2, 3]
>>> rotate_list_right([5, 3, 3, 1], 1)
[1, 5, 3, 3]

列表列表

平均温度(再次)

这个问题就像我们在期中考试中看到的列表问题,但通过列表的列表变得更好!假设你得到一个列表的列表,其中每个内部列表包含一年中所有12个月的平均温度(浮点数)。我们想要找到每年的平均温度,并形成这些年度平均值的列表。

实现函数 annual_temps(nested),它接收每月温度列表的嵌套列表,并返回每年平均温度的(非嵌套)列表。

📋 示例

例如,给定以下列表:

month_values = [[1,2,3,4,5,6,7,8,9,10,11,12],
                [3,0,0,0,0,0,0,0,0,0,0,0],
                [12,0,0,0,0,0,0,0,0,0,0,0]]

annual_temps(nested) 应该返回:

[6.5,
 0.25,
 1.0]

烟火

我们经常将列表的列表视为2D网格,因为它们有两个可以索引的维度。我们可以将列表的列表中的位置称为具有x和y值的坐标。例如,如果我们有这样一个网格:

grid = [[None, None, None],
        [None, None, None],
        [None, 'f' , None],
        [None, None, None]]

我们可以通过 grid[2][1] 访问这个网格中的 'f',其中我们将2视为y坐标,1视为x坐标。

在这个问题中,我们将使用列表的列表来表示具有某些物理特性的场景(就像在Sand中一样)。假设我们的列表的列表(或网格)中的每个位置可以包含由 'f' 表示的烟花、由 's' 表示的闪光,或者 None。我们想要遍历网格中的每个位置,让任何烟花"爆炸",将该位置的 'f' 变为 None,并在该x、y位置的左侧、右侧、顶部和底部放置闪光 's'。例如,爆炸后,上面的网格应该看起来像这样:

[[None, None, None],
 [None, 's' , None],
 ['s' , None, 's' ],
 [None, 's' , None]]

编写函数 explode_fireworks(grid),它接收列表的列表 grid 并遍历该网格的坐标,爆炸任何烟花。你可以假设烟花间隔足够远,这样闪光就不会重叠。如果烟花放置在网格的边缘,以至于它的一些闪光会超出网格的边界,请不要在超出边界的位置绘制任何闪光。

📋 另一个示例

以下是调用函数 explode_fireworks 前后网格的另一个示例:

# before
[[None, 'f' , None, None, None],
 [None, None, None, None, 'f' ],
 [None, None, None, None, None],
 [None, 'f' , None, None, None]]
# after
[['s' , None, 's' , None, 's' ],
 [None, 's' , None, 's' , None],
 [None, 's' , None, None, 's' ],
 ['s' , None, 's' , None, None]]

编写函数 explode_fireworks(grid),接收一个列表列表,遍历每个位置,让“f”烟花爆炸,将该位置变为None,并在上下左右放置“s”闪光。边界外不放闪光。


图像

变暗

编写函数 darken(filename),接收图像文件名 filename,通过将每个像素的 R、G、B 值减半来返回该图像的变暗版本。以下是原始图像可能的样子以及该图像变暗版本的示例。

原始图片
原始图片
变暗后的图片
变暗后的图片

RGB 色调

编写函数 tinted(filename),接收图像文件名 filename,返回一个图像,其中图像的每个垂直三分之一分别具有红色、绿色和蓝色色调。要将像素着色为某种颜色,请将其他颜色值设置为0。例如,要将像素着色为红色,我们将该像素的绿色和蓝色值设置为0,保持红色不变。x值小于图像宽度三分之一的像素应着色为红色,小于图像宽度三分之二(但不小于三分之一)的像素应着色为绿色,其余像素应着色为蓝色。以下是原始图像与着色版本对比的示例。

原始图片
原始图片
RGB三色调图片
RGB三色调效果

字符串

密码生成器

编写函数 make_password(str),接收字符串 str,通过用相似外观的特殊字符替换常见字母来将其转换为密码。任何 a 应该变成 @,i 应该变成 !,o 应该变成 0。这应该是不区分大小写的,这意味着这些字母的大写版本也应该转换为它们的特殊字符。但是,请确保返回的值中原本就是大写的任何其他字符保持大写。

📋 以下是该函数的几个示例调用
>>> make_password('HI!')
'H!!'
>>> make_password('this is a good password')
'th!s !s @ g00d p@ssw0rd'

最长的辅音

编写函数 longest_consonants(str),接收字符串 str,返回连续出现的最长辅音数。我们将辅音视为不是 A、E、I、O 或 U 的字母。如果 str 不包含任何辅音,你应该返回 0。你的计数应该不区分大小写,这意味着你的函数在计算辅音时不区分大写或小写字母。字符串中也可能有非字母字符,这些字符不算作辅音。

提示:要检查一个字符是否为 A、E、I、O 或 U,你可以检查该字符是否在字符串 'AEIOU' 中。

以下是该函数的几个示例调用:

📋 示例
>>> longest_consonants('rhythm')
6
>>> longest_consonants('what is the longest consonant here')
2
>>> longest_consonants('aaaaaa')
0

字典

成绩提升

假设 CS106A 的教师们随机决定给名字以某个字母开头的学生加5分。他们将每个学生的考试成绩存储在一个字典中,其中键是学生姓名,值是该学生获得的分数。例如:

students = {
    'Elyse': 5,
    'Chris': 20,
    'Mehran': 20,
    'Clinton': 40,
    'Neel': 12
}

如果教师们选择提升名字以字母 'C' 开头的学生,这个字典将变成:

students = {
    'Elyse': 5,
    'Chris': 25,
    'Mehran': 20,
    'Clinton': 45,
    'Neel': 12
}

编写函数 grade_boost(students, letter),接收一个如上所示的学生字典和一个表示要提升哪些学生姓名的字母,并更新学生字典以将相应的成绩提升5分。你可以假设给定的字母将是大写的,所有学生姓名都将以大写字母开头。

以下是该函数的示例调用:

📋 示例
>>> grade_boost({'Elyse': 5, 'Chris': 20, 'Mehran': 20, 'Clinton': 40, 'Neel': 12}, 'C')
{'Elyse': 5, 'Chris': 25, 'Mehran': 20, 'Clinton': 45, 'Neel': 12}
>>> grade_boost({'Student1': 10, 'Student2': 15}, 'F')
{'Student1': 10, 'Student2': 15}

动物计数

假设我们让班上的每个人通过在线投票输入动物名称来投票选出他们最喜欢的动物。结果被放入一个文件中,文件的每一行都是一个动物名称和它获得的票数。以下是一些结果:

Dog: 34
Bird: 20
cat: 14
Elephant: 7
dog: 5
CAT: 2

我们想要做的是从这个文件中读取数据到一个字典中,其中键是动物名称(字符串),值是该动物获得的票数(整数)。

不幸的是,投票机制没有考虑到有些人会以不同的方式大写动物名称。注意上面,"Dog" 和 "dog" 各自有自己的分数,但我们真的希望以不区分大小写的方式对每个动物名称的所有票数求和,这样我们就能看到39个人将"dog"作为他们最喜欢的动物。

你的任务是编写一个辅助函数 get_animal_counts(filename),接收一个文件名并返回一个字典,其中重复的计数(对于具有相同字符但不同大小写的键)被合并。新的键应该全部是小写。用上面的文件调用我们的辅助函数将返回:

📋 示例
{
    'dog': 39, 
    'bird': 20, 
    'cat': 16, 
    'elephant': 7
}

嵌套字典

列表转字典

假设你想使用Python存储朋友的电话号码,但你在学习字典之前就这样做了。现在你有一个包含朋友姓名(作为字符串)的大列表,后面跟着他们的电话号码(也是字符串)。你现在意识到你更愿意将这些信息存储为嵌套字典,其中键是姓名,值是电话号码列表。

你有一个可能看起来像这样的列表:

['Noah', '1234567', 'Anooshree', '2347891', '9371924', 'Mel', '2398712']

但你想要将其转换为:

{
    'Noah': ['1234567'], 
    'Anooshree': ['2347891', '9371924'], 
    'Mel': ['2398712']
}

编写函数 list_to_dict(friends_lst),接收朋友姓名和号码的列表,并将其转换为如上所述的嵌套字典。你可以假设电话号码属于它前面的姓名。

提示:你可以使用 .isalpha().isnumeric() 函数来检查列表中的元素是姓名还是号码。

寻找孙子

实现函数 find_grandchildren(parents_to_children),该函数接收字典 parents_to_children,其键是表示父母姓名的字符串,值是该父母的子女姓名列表。创建并返回一个从人们姓名到他们孙子姓名列表的字典。

例如,给定这个字典:

parents_to_children = {
    'Khaled': ['Chibundu', 'Jesmyn'],
    'Daniel': ['Khaled', 'Eve'],
    'Jesmyn': ['Frank'],
    'Eve': ['Grace']
}

Daniel的孙子是Chibundu、Jesmyn和Grace,因为那些是他子女的子女。此外,Khaled是Frank的祖父母,因为Frank的父母是Khaled的子女。因此,调用 find_grandchildren(parents_to_children) 返回这个字典:

{
    'Khaled': ['Frank'],
    'Daniel': ['Chibundu', 'Jesmyn', 'Grace']
}

请注意,不是祖父母的人不会在新词典中显示为键。根据你处理字典的方式,结果的顺序可能不同,但你应该有相同的键和值。


图书馆类Library Class

实现一个具有以下方法的 Library 类:

以下是你的类可能被其他程序使用的方式:

from library import Library

def main():
    green_library = Library()
    green_library.return_book('Karel the Robot Learns Python')
    green_library.return_book('The Giving Tree')
    green_library.return_book('Design as Art')
    print(green_library.get_catalog())          # prints ['Design as Art', 'Karel the Robot Learns Python', 'The Giving Tree']
    green_library.check_out('The Giving Tree')
    green_library.check_out('Annihilation')     # prints No book with that title
    print(green_library.get_catalog())          # prints ['Design as Art', 'Karel the Robot Learns Python']

if __name__ == '__main__':
    main()

以下是你的起始代码:

# File: library.py

class Library:

    def __init__(self):
        """
        Creates a new instance of the Library class
        """
        pass

    def return_book(self, title):
        """
        Returns a book with the given title to the library
        """
        pass

    def check_out(self, title):
        """
        Removes a book with the given title from the library, or prints
        'No book with that title' if that book isn't found
        """
        pass

    def get_catalog(self):
        """
        Returns a list of all books currently in the library, sorted
        alphabetically
        """
        pass

班级类Class Class

不,这不是打字错误。我们要实现一个存储斯坦福大学班级学生信息的类。具体来说,你的 Class 类将具有以下方法:

以下是你的类可能被其他程序使用的方式:

def main():
    cs106a = Class()
    cs106a.add_student('Muhammad', 12345678)
    print(cs106a.get_class_list())      # prints ['Muhammad']
    cs106a.add_student('Elyse', 56781234)
    print(cs106a.get_class_list())      # prints ['Muhammad', 'Elyse']
    print(cs106a.get_id('Maria'))       # prints -1
    print(cs106a.get_id('Muhammad'))    # prints 12345678

if __name__ == '__main__':
    main()

以下是你的起始代码:

# File: Class.py

class Class:

    def __init__(self):
        """
        Creates a new instance of the Class class
        """
        pass

    def add_student(self, name, id):
        """
        Adds a student by name (string) and id (int) to the class
        """
        pass

    def get_id(self, name):
        """
        Returns the id of the student with this name, or -1 if this
        student isn't in the class
        """
        pass

    def get_catalog(self):
        """
        Returns a list of the names of all students enrolled in 
        the class
        """
        pass

披萨类 🍕

实现一个表示披萨饼的 Pizza 类,具有以下方法:

以下是你的类可能被其他程序使用的方式:

from pizza import Pizza

def main():
    veggie = Pizza()
    print(veggie.get_topping_list())  # prints []
    veggie.add_topping('green peppers')
    veggie.add_topping('mushrooms')
    for i in range(8):
        veggie.take_slice()
    print(veggie.get_topping_list())  # ['green peppers', 'mushrooms']
    veggie.take_slice()               # prints No slices left

if __name__ == '__main__':
    main()

以下是你的起始代码:

# File: class.py

class Pizza:

    def __init__(self):
        """
        Creates a new instance of the Pizza class
        """
        pass

    def take_slice(self):
        """
        Takes a slice from the pizza, reducing its number of slices by 1
        """
        pass

    def get_topping_list(self):
        """
        Returns a list of all toppings currently on the pizza
        """
        pass

    def add_topping(self, topping):
        """
        Adds the topping (which is a string) to the pizza, so that all future 
        calls to get_topping_list include this topping
        """
        pass

元组和排序

排序房屋

给定一个表示出租房屋、卧室数量和价格的元组列表,如下所示:

houses = [('main st.', 4, 4000), ('elm st.', 1, 1200), ('pine st.', 2, 1600)]

按以下方式对列表进行排序: