lab整理
lab 整理
刚开始上手确实很懵逼,渐渐搞懂了。
lab00
只需要直接返回 2024 即可。
def twenty_twenty_four(): |
lab01
WWPD
===================================================================== |
答案解释
- 函数调用输出
def welcome(): |
- 预期输出:
(line 1)? Go
:调用welcome()
函数时,它打印“Go”,所以第一行输出“Go”。(line 2)? 'hello'
:welcome()
函数返回字符串'hello'
,但在这里没有直接打印出来。
- 打印函数输出
print(welcome(), cal()) |
- 预期输出:
(line 1)? Go
:第一次调用welcome()
打印“Go”。(line 2)? Bears
:第二次调用cal()
打印“Bears”。(line 3)? hello world
:print
函数将两个函数的返回值组合在一起,分别是'hello'
和'world'
。最终输出是“hello world”。
- 追溯最近的函数调用
- 这个问题询问追溯中最近的函数调用是什么。
- 正确答案是
2) h(x + y * 5)
,因为这是在出现错误之前最后被调用的函数。
- 正确答案是
- 错误的原因
- 追溯中显示了由于使用不兼容的类型而导致的
TypeError
。- 正确答案是
0) the code attempted to add a string to an integer
(代码尝试将字符串与整数相加)。这是因为调用f
函数时传入了字符串参数,最终导致不兼容操作。
- 正确答案是
- 平方函数的文档测试
- 要断言
square(2) == 4
,正确的文档测试格式是:
''' |
- 正确答案是
3
,因为它正确地展示了如何使用文档测试。
- 何时使用打印语句
- 打印语句通常用于检查代码执行过程中的变量值。
- 正确答案是
0) To investigate the values of variables at certain points in your code
(用于调查代码中特定时刻变量的值)。
- 正确答案是
- 防止自动评分器解读打印语句
- 为了防止自动评分器将打印语句解释为输出,你可以在打印语句前加上
'DEBUG:'。
- 正确答案是
1) Print with 'DEBUG:' at the front of the outputted line
(在输出行前加上 'DEBUG:')。
- 正确答案是
- 打开交互终端以调查失败的测试
- 要以交互方式调查失败的测试,应该使用可以打开交互提示符的命令。
- 正确答案是
2) python3 ok -q sum_digits -i
,它允许进行交互式调试。
- 正确答案是
- 不正确的陈述
- 选项中不正确的陈述是:
3) Code that returns a wrong answer instead of crashing is generally better as it at least works fine
(返回错误答案而不是崩溃的代码通常更好,因为它至少可以正常工作)。这是误导性的,因为错误的输出可能会导致比崩溃更大的问题。
Practice
def digit(n, k): |
总结
简单总结一下:
pow(x,y)
:表示\(x^y\)。middle
这个比较有意思,直接返回表达式。在 python 中max
函数可以不止两个参数。
lab02
WWPD
Windows PowerShell |
答案解释
Lambda the Free
- 函数定义和 Lambda 表达式的区别
- 问题:哪个语句描述了
def
语句和 Lambda 表达式之间的区别? - 答案:
2) A lambda expression does not automatically bind the function that it returns to a name.
(Lambda 表达式不会自动将其返回的函数绑定到名称)。- 解释:
def
语句定义的函数可以直接通过名称调用,而 Lambda 表达式需要被赋值给一个变量才能被调用。
- 解释:
- Lambda 表达式的参数数量
- 问题:给定的 Lambda 表达式
lambda a, b: c + d
有多少个正式参数? - 答案:
0) two
(两个)。- 解释:这个 Lambda 表达式有两个参数
a
和b
。
- 解释:这个 Lambda 表达式有两个参数
- Lambda 表达式的返回表达式执行时间
- 问题:Lambda 表达式的返回表达式何时执行?
- 答案:
1) When the function returned by the lambda expression is called.
(当返回的 Lambda 表达式被调用时)。- 解释:Lambda 表达式本身不会立即执行,只有在其返回的函数被调用时,返回表达式才会被执行。
- Lambda 表达式的输出
lambda x: x |
- 答案:
Function
。- 解释:这是一个 Lambda 表达式,定义了一个带有一个参数的函数,但未被调用。
- 赋值 Lambda 表达式
lambda x: x a = |
- 答案:
5
。- 解释:调用 Lambda 函数
a
并传递参数5
,返回5
。
- 解释:调用 Lambda 函数
- 无参数 Lambda 表达式
lambda: 3)() ( |
- 答案:
3
。- 解释:这是一个无参数的 Lambda 表达式,直接调用返回
3
。
- 解释:这是一个无参数的 Lambda 表达式,直接调用返回
- 返回其他 Lambda 的 Lambda
lambda x, y: lambda: x + y b = |
- 答案:
Function
。- 解释:
b
返回一个新的 Lambda 函数,该函数可以被调用以返回x + y
的结果。
- 解释:
- 调用返回的 Lambda
c() |
- 答案:
12
。- 解释:调用
c
返回8 + 4
,即12
。
- 解释:调用
- 接收函数作为参数的 Lambda
lambda f: f(4) d = |
- 答案:
16
。- 解释:
d
函数接收square
函数作为参数,调用square(4)
返回16
。
- 解释:
The Truth Will Prevail
- 逻辑运算的输出
True and 13 |
- 答案:
13
。- 解释:逻辑与运算符
and
的结果是13
,因为在True
的情况下,and
返回后面的值。
- 解释:逻辑与运算符
- 逻辑或运算的输出
False or 0 |
- 答案:
0
。- 解释:逻辑或运算符
or
返回0
,因为前面的值是False
。
- 解释:逻辑或运算符
- 非运算的输出
not 10 |
- 答案:
False
。- 解释:任何非零值在布尔上下文中都被视为
True
,因此not 10
返回False
。
- 解释:任何非零值在布尔上下文中都被视为
- 非运算的 None 值
not None |
- 答案:
True
。- 解释:
None
在布尔上下文中被视为False
,因此not None
返回True
。
- 解释:
- 含错误的逻辑运算
True and 1 / 0 |
- 答案:
Error
。- 解释:在
and
运算中,由于第二个表达式会导致除以零错误,因此会返回错误。
- 解释:在
- 逻辑或运算中的错误
True or 1 / 0 |
- 答案:
True
。- 解释:由于
or
运算符短路特性,1 / 0
不会被执行,因此返回True
。
- 解释:由于
- 逻辑与的比较
1 and 1 > 0 - |
- 答案:
True
。- 解释:
-1
是True
,且1 > 0
也是True
,所以结果为True
。
- 解释:
- 逻辑或运算
1 or 5 - |
- 答案:
-1
。- 解释:由于
-1
是True
,因此返回-1
。
- 解释:由于
Higher Order Functions
- 函数定义
def cake(): |
- 答案:
beets
。- 解释:调用
cake()
时打印beets
,并返回内部函数pie
。
- 解释:调用
- 检查返回的函数
chocolate |
- 答案:
Function
。- 解释:
chocolate
现在是一个函数(pie
)。
- 解释:
- 调用返回的函数
chocolate() |
- 答案:
sweets
和'cake'
。- 解释:调用
chocolate
函数会打印sweets
,并返回'cake'
。
- 解释:调用
- 多重赋值
more_chocolate, more_cake = chocolate(), cake |
- 答案:
sweets
。- 解释:调用
chocolate()
会打印sweets
。
- 解释:调用
- 返回的值
more_chocolate |
- 答案:
'cake'
。- 解释:
more_chocolate
接收chocolate()
的返回值'cake'
。
- 解释:
- 条件语句的返回
def snake(x, y): |
- 答案:
Function
。- 解释:
cake
和more_cake
是相同的,因此返回chocolate
函数。
- 解释:
- 调用函数返回
10, 20)() snake( |
- 答案:
sweets
和'cake'
。- 解释:调用
snake
返回chocolate
函数,然后调用它打印sweets
,返回'cake'
。
- 解释:调用
- 改变
cake
的值
'cake' cake = |
- 答案:
30
。- 解释:这次
cake
和more_cake
不再相同,因此返回10 + 20
,即30
。
- 解释:这次
Practice
有趣,了解了一下闭包的概念。
from math import gcd |
总结
解释一下最后的
cycle
:这段代码实现了一个 高阶函数,它可以按照指定的次数,依次调用三个函数
f1
、f2
和f3
,形成一个“函数循环”。具体来说,它通过递归来实现这个循环,每次调用时,将函数的顺序轮换,并逐步减少剩余的执行次数。我们来逐步分析这个递归结构:函数结构
cycle(f1, f2, f3)
:cycle
是最外层函数,它接受三个单参数函数f1
,f2
, 和f3
作为输入。- 它返回一个函数
g(n)
,其中n
表示将依次执行f1
,f2
,f3
几次的组合操作。
g(n)
:g
是一个函数,接受一个参数n
。n
表示在输入上要进行几次函数的组合调用。- 它返回一个函数
h(x)
,其中x
是对哪个数执行函数的组合。
h(x)
:h
是最终的函数,它接受参数x
,并在此参数上按照f1
,f2
,f3
的顺序执行操作。- 递归通过
n
来决定执行多少次。
递归逻辑分析
让我们特别关注递归的部分:
def h(x):
if n == 0:
return x
else:
return cycle(f2, f3, f1)(n - 1)(f1(x))这个函数
h(x)
是用来在输入x
上应用函数组合的。if n == 0:
当n == 0
时,表示没有函数要执行了,因此h(x)
直接返回x
,即输入什么输出什么。这是递归的基线条件,防止无限递归。递归的 else 部分:
return cycle(f2, f3, f1)(n - 1)(f1(x))
这行代码非常关键。让我们逐步理解:
f1(x)
:首先调用f1
,对x
执行函数f1
的操作。这个结果将作为递归调用的输入。cycle(f2, f3, f1)
:然后调用cycle
函数,传入f2, f3, f1
,也就是说,下一轮的函数顺序变成了f2, f3, f1
,实现了函数的“轮换”。(n - 1)
:将剩余执行次数n
减 1,递归调用时,会在n-1
次的基础上继续。- 最后,通过递归调用
h(x)
,对x
(已经经过f1
处理的值)继续调用下一轮的函数组合。
例子解析
让我们通过一个例子来理解:
my_cycle = cycle(add1, times2, add3)
创建了一个循环函数
my_cycle
,其中add1
,times2
,add3
按顺序执行。my_cycle(3)(2)
的计算过程:n = 3
,调用add1(2)
,结果是3
。- 递归调用
cycle(times2, add3, add1)(2)(3)
:n = 2
,调用times2(3)
,结果是6
。
- 递归调用
cycle(add3, add1, times2)(1)(6)
:n = 1
,调用add3(6)
,结果是9
。
- 递归调用
cycle(add1, times2, add3)(0)(9)
:n = 0
,直接返回9
。
最终,结果为
9
,这表明三个函数add1 -> times2 -> add3
被依次应用在2
上,得到的最终结果是9
。总结
递归部分的关键点在于:
- 按照函数顺序
f1 -> f2 -> f3
应用它们,但顺序会在递归调用中每次向前轮换。 - 递归基线是
n == 0
,防止无限递归,并确保函数最终返回结果。
闭包的解释
counter
能够捕获变量n
的原因在于 闭包 的概念。Python 支持闭包,允许内部函数(如counter
)捕获并使用外部函数作用域中的变量(如n
)。闭包的解释
在 Python 中,当一个函数定义在另一个函数的内部时,内部函数会记住它所处的外部函数中的变量,即使外部函数已经返回,内部函数仍然可以访问这些变量。
在你的代码中:
def count_cond(condition):
def counter(n):
i = 1
count = 0
while i <= n:
if condition(n, i):
count += 1
i += 1
return count
return countercount_cond
函数返回counter
函数。- 当
count_cond
被调用时,比如count_fives = count_cond(lambda n, i: sum_digits(n * i) == 5)
,Python 创建并返回了counter
函数。 - 尽管
count_cond
函数结束并返回了counter
,但counter
仍然能够访问它在调用时的n
参数,这是因为counter
形成了一个闭包。
为什么
counter
能捕获n
当你调用
count_fives(10)
时,counter(10)
被调用:n = 10
被传递给counter
函数,n
成为counter
的局部变量。- 在
counter
中,每次调用condition(n, i)
,n
这个局部变量会传递给condition
。
由于
n
是counter
函数的参数,Python 将它视为counter
的局部变量,因此它可以被随时捕获和使用。也就是说,counter
捕获并使用了传递给它的n
,这就是为什么n
在内部函数中是可见并可用的。闭包示例
一个简单的闭包示例:
def outer_function(x):
def inner_function(y):
return x + y
return inner_function
closure = outer_function(5)
print(closure(10)) # 输出 15在这个例子中,
outer_function
返回了inner_function
,并且inner_function
捕获了x
,即使outer_function
已经结束。closure(10)
调用时,inner_function
仍然能够访问并使用x = 5
。类似地,在你的代码中,counter
捕获并使用了传入的n
。
lab03
WWPD
Windows PowerShell |
答案解释
Lists
s = [7//3, 5, [4, 0, 1], 2]
s[0]
结果为2
- 解释:
7 // 3
是整数除法,结果为2
。
- 解释:
s[2]
结果为[4, 0, 1]
- 解释:这是列表
s
中的第三个元素,即嵌套列表。
- 解释:这是列表
s[-1]
结果为2
- 解释:负索引
-1
表示列表的最后一个元素。
- 解释:负索引
len(s)
结果为4
- 解释:列表
s
中有四个元素,分别是2
、5
、[4, 0, 1]
和2
。
- 解释:列表
4 in s
结果为False
- 解释:
4
不在列表s
的四个元素中。
- 解释:
4 in s[2]
结果为True
- 解释:
4
在s[2]
中的嵌套列表[4, 0, 1]
中。
- 解释:
s[2] + [3 + 2]
结果为[4, 0, 1, 5]
- 解释:
s[2]
是[4, 0, 1]
,而3 + 2
的结果是5
,合并后形成新的列表。
- 解释:
5 in s[2]
结果为False
- 解释:
5
不在s[2]
的嵌套列表[4, 0, 1]
中。
- 解释:
s[2] * 2
结果为[4, 0, 1, 4, 0, 1]
- 解释:将嵌套列表重复两次,形成新列表。
list(range(3, 6))
结果为[3, 4, 5]
- 解释:
range(3, 6)
生成从3
到5
的数字,转换为列表。
- 解释:
range(3, 6)
结果为range(3, 6)
- 解释:
range
对象的显示形式。
- 解释:
r = range(3, 6)
,[r[0], r[2]]
结果为[3, 5]
- 解释:
r[0]
是3
,r[2]
是5
。
- 解释:
range(4)[-1]
结果为3
- 解释:负索引
-1
获取范围中的最后一个元素。
- 解释:负索引
Comprehensions
[2 * x for x in range(4)]
- 结果为
[0, 2, 4, 6]
- 解释:
range(4)
生成0, 1, 2, 3
,每个值乘以2
后形成新列表。
- 解释:
- 结果为
[y for y in [6, 1, 6, 1] if y > 2]
- 结果为
[6, 6]
- 解释:仅保留大于
2
的值。
- 解释:仅保留大于
- 结果为
[[1] + s for s in [[4], [5, 6]]]
- 结果为
[[1, 4], [1, 5, 6]]
- 解释:将
1
添加到每个嵌套列表的开头。
- 解释:将
- 结果为
[z + 1 for z in range(10) if z % 3 == 0]
- 结果为
[1, 4, 7, 10]
- 解释:仅保留
0, 3, 6, 9
(0
和3
在+1
后形成1
和4
),再加上10
(9
在+1
后形成10
)。
- 解释:仅保留
- 结果为
Practice
LAB_SOURCE_FILE = __file__ |
总结
- 可以学到这个用法:返回一个列表推导式,当
fabs(i - s[i]) <= k
,返回s[i]
。
def close_list(s, k): |
这个需要注意一下精度问题:
round()
函数采用"四舍六入,五取偶"的规则,也称为"银行家舍入法"。具体来说:当需要舍入的位数小于 5 时(如
1, 2, 3, 4
),直接舍去。当需要舍入的位数大于 5 时(如
6, 7, 8, 9
),直接进一。当需要舍入的位数为 5 时,取决于被舍去位之前的数字:
如果舍去位前的数字是偶数,则舍去(向下舍入)。
如果舍去位前的数字是奇数,则进一(向上舍入)。
print(round(2.5)) # 输出 2,舍入为偶数
print(round(3.5)) # 输出 4,舍入为偶数
print(round(1.235, 2)) # 输出 1.24,四舍六入五取偶
print(round(1.245, 2)) # 输出 1.24,五取偶
def squares(s): |
注意到需要两位是 8,不难想到从
n
的最低位进行处理:先判断最低位是不是 8,再看高一位是不是也是 8,如果是,返回True
。如果
n
只有 1 位,肯定不符合题意,返回False
。
def double_eights(n): |
- 这道题目的要求是返回函数
can_reach
:我们需要进行can_reach
函数的编写:要求的是通过小于limit
次的f
和g
函数的操作,实现x==y
,这里采用递归方法。- 临界情况:超过
limit
次,can_reach
函数返回False
。 - 成功情况:
x==y
,返回True
。 - 一般情况:更新
x
,继续进行can_reach
函数操作:有两种情况:- 可能是调用
f
,也可能是调用g
,采取其中一种方法即可,所以两种结果进行or
运算。
- 可能是调用
- 临界情况:超过
def make_onion(f, g): |
lab04
WWPD
===================================================================== |
答案解释
以下是每道题的中文解释:
pokemon['pikachu']
- 答案:
25
- 解释: 这是在字典
pokemon
中通过键'pikachu'
来访问对应的值。键'pikachu'
的值是25
,所以返回25
。
- 答案:
len(pokemon)
- 答案:
3
- 解释:
len()
函数返回字典pokemon
中键-值对的数量。这里有三个键-值对:'pikachu'
、'dragonair'
和'mew'
,所以长度为3
。
- 答案:
'mewtwo' in pokemon
- 答案:
False
- 解释:
in
操作符用于检查'mewtwo'
是否是字典pokemon
中的一个键。由于'mewtwo'
不是键,因此结果为False
。
- 答案:
'pikachu' in pokemon
- 答案:
True
- 解释:
in
操作符用于检查'pikachu'
是否是字典pokemon
中的一个键。因为'pikachu'
是一个键,所以结果为True
。
- 答案:
25 in pokemon
- 答案:
False
- 解释:
in
操作符在字典中用于检查某个键是否存在,而不是值。25
是一个值,但不是键,因此结果为False
。
- 答案:
148 in pokemon.values()
- 答案:
True
- 解释:
pokemon.values()
返回字典中所有值的视图。由于148
是其中一个值(对应键'dragonair'
),结果为True
。
- 答案:
151 in pokemon.keys()
- 答案:
False
- 解释:
pokemon.keys()
返回字典中所有键的视图。151
是一个值而非键,因此结果为False
。
- 答案:
'mew' in pokemon.keys()
- 答案:
True
- 解释:
pokemon.keys()
返回所有键的视图。'mew'
是字典中的一个键,因此结果为True
。
- 答案:
Practice
def divide(quotients, divisors): |
总结
- 还是对列表推导式的一个妙用,另外这里还引入了字典推导式,属于两者的混用。
def divide(quotients, divisors): |
Python 支持字典推导式,其语法类似于列表推导式,但用于生成字典。字典推导式可以让我们通过简洁的方式构造字典。语法格式如下:
{key_expr: value_expr for item in iterable if condition}其中:
key_expr
表示键的表达式。value_expr
表示值的表达式。iterable
是可迭代对象。condition
是可选的条件,用于筛选符合条件的元素。示例
生成简单字典
squares = {x: x**2 for x in range(1, 6)}
print(squares) # 输出: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}带条件的字典推导式
evens = {x: x**2 for x in range(1, 6) if x % 2 == 0}
print(evens) # 输出: {2: 4, 4: 16}从列表生成字典
fruits = ['apple', 'banana', 'cherry']
fruit_lengths = {fruit: len(fruit) for fruit in fruits}
print(fruit_lengths) # 输出: {'apple': 5, 'banana': 6, 'cherry': 6}交换键和值
original = {'a': 1, 'b': 2, 'c': 3}
inverted = {v: k for k, v in original.items()}
print(inverted) # 输出: {1: 'a', 2: 'b', 3: 'c'}字典推导式是 Python 中非常强大的特性,可以让代码更加简洁和可读。
先补充题目的要求:
Implement the
buy
function that takes three parameters: 实现带有三个参数的buy
函数:fruits_to_buy
: A list of strings representing the fruits you need to buy. At least one of each fruit must be bought.fruits_to_buy
:代表您需要购买的水果的字符串列表。每种水果至少要买一个。prices
: A dictionary where the keys are fruit names (strings) and the values are positive integers representing the cost of each fruit.prices
:一个字典,其中键是水果名称(字符串),值是表示每种水果成本的正整数。total_amount
: An integer representing the total money available for purchasing the fruits. Take a look at the docstring for more details on the input structure.total_amount
:一个整数,表示可用于购买水果的总金额。有关输入结构的更多详细信息,请查看文档字符串。
The function should print all possible ways to buy the required fruits so that the combined cost equals
total_amount
. You can only select fruits mentioned infruits_to_buy
list. 该函数应该打印购买所需水果的所有可能方式,以便总成本等于total_amount
。您只能选择fruits_to_buy
列表中提到的水果。Note: You can use the
display
function to format the output. Calldisplay(fruit, count)
for each fruit and its corresponding quantity to generate a string showing the type and amount of fruit bought. 注意:您可以使用display
功能来格式化输出。对每个水果及其对应的数量调用display(fruit, count)
,生成一个字符串,显示购买的水果的类型和数量。Hint: How can you ensure that every combination includes at least one of each fruit listed in
fruits_to_buy
? 提示:如何确保每个组合至少包含fruits_to_buy
中列出的每种水果中的一种?解释:
cart
这里是要输出的表达式,需要留意。临界情况就是:水果种类用完和钱用完的情况,输出
cart
一般情况:计算当前水果
fruit[0]
的所有可能情况(花费的金额小于amout
),然后计算接下来水果的购买情况。需要留意的是更新
cart
,使用cart + display(fruit, k)
进行格式化。
def buy(fruits_to_buy, prices, total_amount): |
lab05
WWPD1
===================================================================== |
答案解释 1
以下是每道题的解释:
print(s.append(6))
- 答案:
None
- 解释:
append
方法将6
添加到列表s
的末尾,但它本身不返回任何值(返回None
),因此print
输出None
。
- 答案:
s
- 答案:
[6, 7, 8, 6]
- 解释: 刚才
append(6)
将6
添加到了s
的末尾,所以s
现在是[6, 7, 8, 6]
。
- 答案:
s.insert(0, 9)
和s
- 答案:
[9, 6, 7, 8, 6]
- 解释:
insert(0, 9)
在索引0
位置插入9
,使s
变为[9, 6, 7, 8, 6]
。
- 答案:
x = s.pop(1)
和s
- 答案:
[9, 7, 8, 6]
- 解释:
pop(1)
删除并返回索引1
位置的元素6
,使得s
更新为[9, 7, 8, 6]
,且x
为6
。
- 答案:
s.remove(x)
和s
- 答案:
[9, 7, 8]
- 解释:
remove(x)
从列表中移除第一个值为x
的元素,即6
,所以s
变为[9, 7, 8]
。
- 答案:
a, b = s, s[:]
和a is s
- 答案:
True
- 解释:
a
和s
指向同一个对象,因此a is s
为True
。
- 答案:
b == s
- 答案:
True
- 解释:
b
是s
的一个副本,因此b
的值与s
相等,所以b == s
为True
。
- 答案:
b is s
- 答案:
False
- 解释:
b
是s
的一个浅拷贝,不是同一个对象,因此b is s
为False
。
- 答案:
a.pop()
- 答案:
8
- 解释:
pop()
移除并返回列表的最后一个元素8
,所以结果是8
。
- 答案:
a + b
- 答案:
[9, 7, 9, 7, 8]
- 解释:
a
现在是[9, 7]
,而b
是[9, 7, 8]
。a + b
是两个列表的连接,结果为[9, 7, 9, 7, 8]
。
- 答案:
s = [3]
和s.extend([4, 5])
和s
- 答案:
[3, 4, 5]
- 解释:
extend
方法将[4, 5]
添加到s
的末尾,所以s
变为[3, 4, 5]
。
- 答案:
a
- 答案:
[9, 7]
- 解释:
a
没有被修改过,所以仍然是[9, 7]
。
- 答案:
s.extend([s.append(9), s.append(10)])
和s
- 答案:
[3, 4, 5, 9, 10, None, None]
- 解释:
s.append(9)
和s.append(10)
各自会将9
和10
添加到列表s
中,但append
方法返回None
,所以s.extend([None, None])
也会添加两个None
。最终,s
为[3, 4, 5, 9, 10, None, None]
。
- 答案:
WWPD2
===================================================================== |
答案解释 2
以下是每道题的中文解释:
Case 1
next(s)
- 答案:
Error
- 解释:
next()
需要一个迭代器对象,但s
是一个列表,不是迭代器,所以会引发TypeError
。
- 答案:
next(t)
- 答案:
1
- 解释:
t
是s
的迭代器,调用next(t)
返回s
中的第一个元素1
。
- 答案:
next(t)
- 答案:
2
- 解释: 再次调用
next(t)
返回s
中的第二个元素2
。
- 答案:
next(iter(s))
- 答案:
1
- 解释:
iter(s)
创建s
的新迭代器,调用next()
返回第一个元素1
。
- 答案:
next(iter(s))
- 答案:
1
- 解释: 同上,再次创建
s
的新迭代器,next()
返回第一个元素1
。
- 答案:
next(t)
- 答案:
3
- 解释:
t
已经迭代到s
中的第三个元素3
,因此next(t)
返回3
。
- 答案:
next(t)
- 答案:
4
- 解释:
t
现在返回s
中的第四个元素4
。
- 答案:
Case 2
next(r_iter)
- 答案:
0
- 解释:
r_iter
是range(6)
的迭代器,next(r_iter)
返回第一个元素0
。
- 答案:
[x + 1 for x in r]
- 答案:
[1, 2, 3, 4, 5, 6]
- 解释: 列表推导式会对
r
的每个元素加1
,结果是[1, 2, 3, 4, 5, 6]
。
- 答案:
[x + 1 for x in r_iter]
- 答案:
[2, 3, 4, 5, 6]
- 解释: 因为
r_iter
已经被迭代过一次,当前指向1
后的元素,故结果为[2, 3, 4, 5, 6]
。
- 答案:
next(r_iter)
- 答案:
StopIteration
- 解释:
r_iter
已经被完全迭代,调用next()
时会引发StopIteration
。
- 答案:
Case 3
next(map_iter)
- 答案:
10
- 解释:
map_iter
应用lambda x: x + 10
到range(5)
,第一个元素是10
。
- 答案:
next(map_iter)
- 答案:
11
- 解释:
map_iter
的下一个元素是1 + 10 = 11
。
- 答案:
list(map_iter)
- 答案:
[12, 13, 14]
- 解释:
map_iter
已经迭代到2
开始的元素,剩余元素12
、13
和14
转换为列表。
- 答案:
for e in filter(lambda x: x % 4 == 0, range(1000, 1008))
- 答案:
1000
,1004
- 解释:
filter
返回range(1000, 1008)
中能被4
整除的数,所以打印1000
和1004
。
- 答案:
[x + y for x, y in zip([1, 2, 3], [4, 5, 6])]
- 答案:
[5, 7, 9]
- 解释:
zip
配对两个列表元素,逐对相加,得到[5, 7, 9]
。
- 答案:
这些题目测试了迭代器和生成器在 Python 中的使用,以及如何应用
next()
函数和 map
、filter
等函数的操作。
Practice
HW_SOURCE_FILE=__file__ |
总结
- 这道题需要留意使用
s.insert(index, value)
,表示在index
位置插入value
值,需要留意的是由于插入后发生了修改,所以需要多进行一次index += 1
。
def insert_items(s, before, after): |
有个小细节:注意这里是在字典中创建列表,所以当
key
不在列表中时,需要手动创建一个列表。如果
key
已经在列表中:直接添加到列表末尾即可。
def group_by(s, fn): |
这道题需要看一下前后提示:
Implement
repeated
, which takes in an iteratort
and an integerk
greater than 1. It returns the first value int
that appearsk
times in a row. You may assume that there is an element oft
repeated at leastk
times in a row. 实现repeated
,它接受一个迭代器t
和一个大于 1 的整数k
。它返回t
中连续出现k
次的第一个值。您可以假设t
中有一个元素连续重复至少k
次。Important: Call
next
ont
only the minimum number of times required. If you are receiving aStopIteration
exception, yourrepeated
function is callingnext
too many times. 重要提示:仅调用next
ont
所需的最少次数。如果您收到StopIteration
异常,则说明您的repeated
函数调用next
次数过多。因为已经保证是有解的了:所以可以这样实现:
记录一个
last_val
,如果当前val
和last_val
相同,更新记录cnt
,否则进行重置,直到cnt==k
为止。不需要担心收到
StopIteration
异常。
def repeated(t, k): |
注意这里的
tree
本质上还是一个列表,我们围绕列表进行分析:临界情况:根据题意在叶子进行添加树,当当前节点是叶子时,可以使用列表推导式对其进行扩展(使用
tree
的构造方法)。一般情况:我们对当前节点的每个分支进行递归处理即可,可以使用列表推导式完成
def sprout_leaves(t, leaves): |
- 使用交换即可,通过双指针实现:
def partial_reverse(s, start): |
lab06
无WWPD
。
Practice
class Transaction: |
总结
Transaction
不难,关键在于BankAccount
的修改:可以留意的是需要进行交易记录的保存,所以我们添加一个self.transactions = []
,由于还需要记录交易记录ID
,所以设计一个函数next_id
,和一个变量self.count
,用于跟踪交易次数。deposit
和withdraw
函数根据要求实现即可,没有什么值得特别留意的地方。
class Transaction: |
主要是
create
方法:需要调用coin
构造函数创建一个对象,并且返回该实例。worth
需要留意的是不要修改它的原始价值,返回的价值是根据年份差计算得到的。
class Mint: |
lab07
WWPD
===================================================================== |
其实还有,但是忘记截取下来了,重要性也比较低。
答案解释
以下是每道题的解释:
print(A.x, B.x, C.x)
- 答案:
0 0 0
- 解释:
A
是基类,B
和C
是继承A
的子类。初始时,x
的值在所有类中都是0
。
- 答案:
B.x = 2
- 答案:
0 2 0
- 解释: 这一行代码将
B
类中的x
属性设置为2
,此时A
和C
中的x
值保持不变,分别为0
。
- 答案:
A.x += 1
- 答案:
1 2 1
- 解释:
A.x += 1
修改A
类的x
值,将其增加到1
。由于B
有独立的x
值,因此不受影响,B.x
依然为2
。C
类继承A
的x
,所以其x
值也更新为1
。
- 答案:
obj = C()
和obj.y = 1
- 答案:
False
- 解释:
obj
是C
类的一个实例。通过obj.y = 1
,我们为该实例obj
的y
属性赋值为1
,这是obj
的一个实例属性,不会影响类属性C.y
,所以C.y == obj.y
为False
。
- 答案:
A.y = obj.y
和print(A.y, B.y, C.y, obj.y)
- 答案:
1 1 1 1
- 解释:
A.y = obj.y
将A
类的y
属性更新为1
。因为B
和C
类没有自己独立的y
,它们会继承A
的y
值,所以B.y
和C.y
也变为1
。obj.y
是实例属性,也为1
。
- 答案:
Practice
class Account: |
总结
这个得先看一下题意:不然做起来稀里糊涂。
Add a
time_to_retire
method to theAccount
class. This method takes in anamount
and returns the number of years until the currentbalance
grows to at leastamount
, assuming that the bank adds the interest (calculated as the currentbalance
multiplied by theinterest
rate) to thebalance
at the end of each year. Make sure you're not modifying the account's balance! 将time_to_retire
方法添加到Account
类。此方法接受amount
并返回当前balance
增长到至少amount
之前的年数,假设银行在每年年底将利息(计算为当前balance
乘以interest
)添加到balance
中。确保您没有修改帐户余额!总结就是注意利息的计算方法。
class Account: |
这里关键是修改
withdraw
函数和__init__
函数,由于FreeChecking
是Account
的子类,所以可以通过调用Account
的构造函数进行初始化,此外由于需要跟踪免费的次数,所以设置另外一个属性self.withdraws = 0
。withdraw
函数需要重新实现:每次更新self.withdraws
,一旦免费次数用完就修改fee
,否则为 0,后调用父类的withdraw
函数即可。
class FreeChecking(Account): |
- 创建链表可以使用头插法或者尾插法,这里使用头插法:
- 临界情况:链表用完,返回
Link.empty
- 目标情况:到达位置
i
,直接忽略,接上尾部即可。 - 一般情况:递归创建链表,注意更新
i - 1
。
- 临界情况:链表用完,返回
def without(s, i): |
不是新链表,只能在原来的基础上进行改动:
这里使用头插法比较方便:因为可以保留
s.rest
,只需要在s.first
后面插入,s.rest
前面插入。遇到s.first==val
时进行处理,后面正常递归调用即可。
def duplicate_link(s, val): |
lab08
===================================================================== |
答案解释
t = Tree(1, Tree(2))
- 答案:
Error
- 解释:
Tree
构造函数的第二个参数应为一个包含Tree
实例的列表。在此语句中,Tree(2)
不是列表,因此会引发错误。
- 答案:
t = Tree(1, [Tree(2)])
和t.label
- 答案:
1
- 解释:
t
是一个树,其根节点标签为1
,唯一的子树是Tree(2)
。所以,t.label
返回根节点的标签,即1
。
- 答案:
t.branches[0]
- 答案:
Tree(2)
- 解释:
t.branches
是一个包含Tree(2)
的列表,t.branches[0]
返回第一个子树对象,即Tree(2)
。
- 答案:
t.branches[0].label
- 答案:
2
- 解释:
t.branches[0]
是Tree(2)
,它的label
属性为2
。
- 答案:
t.label = t.branches[0].label
和t
- 答案:
Tree(2, [Tree(2)])
- 解释:
t.label = t.branches[0].label
将根节点的标签从1
改为2
,所以t
现在的结构是Tree(2, [Tree(2)])
。
- 答案:
t.branches.append(Tree(4, [Tree(8)]))
和len(t.branches)
- 答案:
2
- 解释:
append
操作在t
的branches
列表中添加了一个新的子树Tree(4, [Tree(8)])
。t
现在有两个分支,因此len(t.branches)
为2
。
- 答案:
t.branches[0]
- 答案:
Tree(2)
- 解释:
t.branches[0]
是第一个子树,保持为Tree(2)
。
- 答案:
t.branches[1]
- 答案:
Tree(4, [Tree(8)])
- 解释:
t.branches[1]
是第二个子树,新添加的Tree(4, [Tree(8)])
。
- 答案:
Practice
def cumulative_mul(t): |
总结
- 递归调用:先更新叶子,后往上一层一层更新父亲。
def cumulative_mul(t): |
这个需要留意的是从父亲到儿子进行处理,所以先对当前节点的分支进行剪切,然后再递归到下一层逐层处理。
补充题目背景如下:
Removing some nodes from a tree is called pruning the tree. 从树中删除一些节点称为修剪树。
Complete the function
prune_small
that takes in aTree
t
and a numbern
. For each node with more thann
branches, keep only then
branches with the smallest labels and remove (prune) the rest. 完成函数prune_small
,它接受Tree
t
和数字n
。对于每个具有超过n
分支的节点,仅保留具有最小标签的n
分支,并删除(修剪)其余的。Hint: The
max
function takes in aniterable
as well as an optionalkey
argument (which takes in a one-argument function). For example,max([-7, 2, -1], key=abs)
would return-7
sinceabs(-7)
is greater thanabs(2)
andabs(-1)
. 提示:max
函数接受一个iterable
以及一个可选的key
参数(它接受一个单参数函数)。例如,max([-7, 2, -1], key=abs)
将返回-7
因为abs(-7)
大于abs(2)
和abs(-1)
。
def prune_small(t, n): |
大致思路是创建一个
new_braches
,替换t.branches
。Implement
delete
, which takes a Treet
and removes all non-root nodes labeledx
. The parent of each remaining node is its nearest ancestor that was not removed. The root node is never removed, even if its label isx
. 实现delete
,它采用 Treet
并删除所有标记为x
非根节点。每个剩余节点的父节点是其未删除的最近祖先。根节点永远不会被删除,即使它的标签是x
。注意,树不会断!!!所以就算删去了标记为
x
的非根节点,还是需要对分支进行处理。先递归处理好儿子,再返回处理祖先。所以第三个空是
delete
函数,后面就是new_branches
的拼接:需要留意append
和extend
方法的区别。append
不管元素是否为空拼接到末尾,extend
是将iterable
中的非空元素拼接到末尾
def delete(t, x): |