作业 1:函数、控制、高阶函数

截止时间:6月30日(周二)23:59

说明

下载 hw01.zip

提交:完成后,将作业提交到 Gradescope。你可以在截止日期前多次提交;只有最终提交会被评分。请确认你已在 Gradescope 上成功提交代码。 有关提交作业的更多说明,请参见实验 0

使用 Ok:如果你对使用 Ok 有任何疑问,请参阅此指南。

阅读材料:你可能会发现以下参考资料有用:

评分:作业根据正确性评分。每道错题将使总分减少一分。 本次作业满分为 2 分。

必做题

函数与控制

Q1:A Plus Abs B

Python 的 operator 模块包含双参数函数,如 addsub,对应 Python 的内置算术运算符。例如,add(2, 3) 的结果为 5,就像表达式 2 + 3 一样。

填写以下函数中的空白处,将 a 加上 b 的绝对值,但不调用 abs 函数。除了两个空白处外,你不得修改任何已提供的代码。

def a_plus_abs_b(a, b):
    """Return a+abs(b), but without calling abs.

    >>> a_plus_abs_b(2, 3)
    5
    >>> a_plus_abs_b(2, -3)
    5
    >>> a_plus_abs_b(-1, 4)
    3
    >>> a_plus_abs_b(-1, -4)
    3
    """
    if b < 0:
        f = _____
    else:
        f = _____
    return f(a, b)

使用 Ok 测试你的代码:

python3 ok -q a_plus_abs_b

使用 Ok 运行本地语法检查器(检查你是否修改了两个空白处以外的已提供代码):

python3 ok -q a_plus_abs_b_syntax_check

Q2:Hailstone

Douglas Hofstadter 的普利策奖获奖著作《哥德尔、艾舍尔、巴赫》提出了以下数学谜题。

  1. 选取一个正整数 n 作为起点。
  2. 如果 n 是偶数,将其除以 2。
  3. 如果 n 是奇数,将其乘以 3 再加 1。
  4. 重复此过程直到 n 为 1。

数字 n 会上下波动,但最终会到达 1(至少对于所有尝试过的数字来说是这样——没有人证明过该序列一定会终止)。类似地,冰雹在大气中上下运动,最终落到地面上。

这个 n 的值序列通常称为冰雹序列。编写一个函数,接受一个参数,形式参数名为 n,打印从 n 开始的冰雹序列,并返回序列的步数:

def hailstone(n):
    """Print the hailstone sequence starting at n and return its
    length.

    >>> a = hailstone(10)
    10
    5
    16
    8
    4
    2
    1
    >>> a
    7
    >>> b = hailstone(1)
    1
    >>> b
    1
    """
    "*** YOUR CODE HERE ***"

冰雹序列可能会很长!试试 27。你能找到最长的序列吗?

注意:如果 n == 1 初始时就是 1,则序列长度为一步。
提示:如果你看到 4.0 但只想要 4,尝试使用向下取整除法 // 而不是普通除法 /

使用 Ok 测试你的代码:

python3 ok -q hailstone

对冰雹序列感到好奇?看看这篇文章:

  • 2019年,在理解冰雹猜想对大多数数字的适用性方面取得了重大进展

高阶函数

Q3:Product

编写一个名为 product 的函数,返回一个序列前 n 项的乘积。具体来说,product 接受一个整数 ntermterm 是一个单参数函数,用于确定一个序列(即 term(i) 给出序列的第 i 项)。 product(n, term) 应返回 term(1) * ... * term(n)

def product(n, term):
    """Return the product of the first n terms in a sequence.

    n: a positive integer
    term: a function that takes an index as input and produces a term

    >>> product(3, identity)  # 1 * 2 * 3
    6
    >>> product(5, identity)  # 1 * 2 * 3 * 4 * 5
    120
    >>> product(3, square)    # 1^2 * 2^2 * 3^2
    36
    >>> product(5, square)    # 1^2 * 2^2 * 3^2 * 4^2 * 5^2
    14400
    >>> product(3, increment) # (1+1) * (2+1) * (3+1)
    24
    >>> product(3, triple)    # 1*3 * 2*3 * 3*3
    162
    """
    "*** YOUR CODE HERE ***"

使用 Ok 测试你的代码:

python3 ok -q product

Q4:Make Repeater

实现函数 make_repeater,它接受一个单参数函数 f 和一个正整数 n。它返回一个单参数函数,使得 make_repeater(f, n)(x) 返回 f(f(...f(x)...)) 的值,其中 fx 应用了 n 次。例如,make_repeater(square, 3)(5) 对 5 平方三次并返回 390625,就像 square(square(square(5))) 一样。

def make_repeater(f, n):
    """Returns the function that computes the nth application of f.

    >>> add_three = make_repeater(increment, 3)
    >>> add_three(5)
    8
    >>> make_repeater(triple, 5)(1) # 3 * (3 * (3 * (3 * (3 * 1))))
    243
    >>> make_repeater(square, 2)(5) # square(square(5))
    625
    >>> make_repeater(square, 3)(5) # square(square(square(5)))
    390625
    """
    "*** YOUR CODE HERE ***"

使用 Ok 测试你的代码:

python3 ok -q make_repeater

本地检查你的分数

你可以通过运行以下命令在本地检查本作业每道题的分数:

python3 ok --score

这不会提交作业!当你对分数满意后,将作业提交到 Gradescope 以获得分数。

提交作业

通过上传你编辑过的文件到相应的 Gradescope 作业来提交本作业。实验 00有详细说明。

选做题

Q5:Largest Factor

编写一个函数,接受一个大于 1的整数 n,返回小于 n 且能整除 n 的最大整数。

def largest_factor(n):
    """Return the largest factor of n that is smaller than n.

    >>> largest_factor(15) # factors are 1, 3, 5
    5
    >>> largest_factor(80) # factors are 1, 2, 4, 5, 8, 10, 16, 20, 40
    40
    >>> largest_factor(13) # factors are 1, 13
    1
    """
    "*** YOUR CODE HERE ***"

提示:要检查 b 是否能整除 a,使用表达式 a % b == 0,可以理解为"a 除以 b 的余数为 0。"

使用 Ok 测试你的代码:

python3 ok -q largest_factor

Q6:Accumulate

让我们来看看 product 如何作为一个更通用的函数 accumulate 的实例,我们想要实现这个函数:

def accumulate(fuse, start, n, term):
    """Return the result of fusing together the first n terms in a sequence 
    and start.  The terms to be fused are term(1), term(2), ..., term(n). 
    The function fuse is a two-argument commutative & associative function.

    >>> accumulate(add, 0, 5, identity)  # 0 + 1 + 2 + 3 + 4 + 5
    15
    >>> accumulate(add, 11, 5, identity) # 11 + 1 + 2 + 3 + 4 + 5
    26
    >>> accumulate(add, 11, 0, identity) # 11 (fuse is never used)
    11
    >>> accumulate(add, 11, 3, square)   # 11 + 1^2 + 2^2 + 3^2
    25
    >>> accumulate(mul, 2, 3, square)    # 2 * 1^2 * 2^2 * 3^2
    72
    >>> # 2 + (1^2 + 1) + (2^2 + 1) + (3^2 + 1)
    >>> accumulate(lambda x, y: x + y + 1, 2, 3, square)
    19
    """
    "*** YOUR CODE HERE ***"

accumulate 有以下参数:

  • fuse:一个双参数函数,指定当前项如何与之前累积的项合并
  • start:开始累积的值
  • n:一个非负整数,表示要合并的项数
  • term:一个单参数函数;term(i) 是序列的第 i

实现 accumulate,使用 fuse 函数将 term 定义的序列的前 n 项与 start 值合并。

例如,accumulate(add, 11, 3, square) 的结果是

add(11,  add(square(1), add(square(2),  square(3)))) =
    11 +     square(1) +    square(2) + square(3)    =
    11 +     1         +    4         + 9            = 25

假设 fuse 是可交换的,fuse(a, b) == fuse(b, a),且可结合的,fuse(fuse(a, b), c) == fuse(a, fuse(b, c))

使用 Ok 测试你的代码:

python3 ok -q accumulate

然后,将 summationproduct 实现为对 accumulate 的单行调用。

重要:summation_using_accumulateproduct_using_accumulate 都应该用一行以 return 开头的代码实现。

def summation_using_accumulate(n, term):
    """Returns the sum: term(1) + ... + term(n), using accumulate.

    >>> summation_using_accumulate(5, square) # square(1) + square(2) + ... + square(4) + square(5)
    55
    >>> summation_using_accumulate(5, triple) # triple(1) + triple(2) + ... + triple(4) + triple(5)
    45
    >>> # This test checks that the body of the function is just a return statement.
    >>> import inspect, ast
    >>> [type(x).__name__ for x in ast.parse(inspect.getsource(summation_using_accumulate)).body[0].body]
    ['Expr', 'Return']
    """
    return ____

def product_using_accumulate(n, term):
    """Returns the product: term(1) * ... * term(n), using accumulate.

    >>> product_using_accumulate(4, square) # square(1) * square(2) * square(3) * square()
    576
    >>> product_using_accumulate(6, triple) # triple(1) * triple(2) * ... * triple(5) * triple(6)
    524880
    >>> # This test checks that the body of the function is just a return statement.
    >>> import inspect, ast
    >>> [type(x).__name__ for x in ast.parse(inspect.getsource(product_using_accumulate)).body[0].body]
    ['Expr', 'Return']
    """
    return ____

使用 Ok 测试你的代码:

python3 ok -q summation_using_accumulate
python3 ok -q product_using_accumulate