CS 61A Spring 2019 HW02 学习笔记

CS 61A: Structure and Interpretation of Computer Programs 课程官网
Homework 2: Higher Order Functions 作业链接 参考答案

主要内容

  1. 匿名函数;
  2. 柯里化;
  3. 函数作为参数;
  4. 函数作为返回值;
  5. 函数嵌套。

可能用到的函数

from operator import add, mul, sub

square = lambda x: x * x

identity = lambda x: x

triple = lambda x: 3 * x

increment = lambda x: x + 1

Q1: Make Adder with a Lambda

Implement the make_adder function, which takes in a number n and returns a function that takes in an another number k and returns n + k. Your solution must consist of a single return statement.

def make_adder(n):
    """Return a function that takes an argument K and returns N + K.

    >>> add_three = make_adder(3)
    >>> add_three(1) + add_three(2)
    9
    >>> make_adder(1)(2)
    3
    """
    return 'YOUR EXPRESSION HERE'

我的解答(同参考答案):

	return lambda k: n + k 

NOTE

lambda表达式

使用lambda表达式定义匿名函数,冒号前为匿名函数的形式参数(输入),冒号后为返回值(输出)。

柯里化

本题代码将add(n, k)转化为make_adder(n)(k),这种变形称为柯里化。

柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
百度百科

make_adder(n)函数接受参数n,并返回接受参数k的匿名函数,匿名函数返回n + k的结果。
柯里化在JS中用得比较多。
相关资料:
简述几个非常有用的柯里化函数使用场景
为什么柯里化有用(下午茶译文赏析)

Q2: Product

The summation(n, term) function from the higher-order functions lecture adds up term(1) + ... + term(n). Write a similar function called product that returns term(1) * ... * term(n). Do not use recursion.

def product(n, term):
    """Return the product of the first n terms in a sequence.
    n    -- a positive integer
    term -- a function that takes one argument

    >>> 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
    >>> from construct_check import check
    >>> check(HW_SOURCE_FILE, 'product', ['Recursion'])
    True
    """
    "*** YOUR CODE HERE ***"

我的解答:

    result = 1
    while n > 0:
        result *= term(n)
        n -= 1
    return result

参考答案:

    total, k = 1, 1
    while k <= n:
        total, k = term(k) * total, k + 1
    return total

NOTE

product(n, term)的形参term接收有一个参数的函数,也就是将函数作为参数输入到product()。这样就将“求term(1)-term(n)乘积”这个动作抽象成了函数product(),不管是求二次方的乘积、三次方的乘积,都可以直接调用product(),只需要将term修改为对应的计算函数。

Q3: Accumulate

Let’s take a look at how summation and product are instances of a more general function called accumulate

def accumulate(combiner, base, n, term):
    """Return the result of combining the first n terms in a sequence and base.
    The terms to be combined are term(1), term(2), ..., term(n).  combiner 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
    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
    >>> accumulate(lambda x, y: x + y + 1, 2, 3, square)
    19      #(((2 + 1^2 + 1) + 2^2 + 1) + 3^2 + 1)
    """
    "*** YOUR CODE HERE ***"

我的解答:

    result = base
    while n > 0:
        # result = combiner(result, term(n))
        result = combiner(term(n), result)  # compatible with compose1()
        n -= 1
    return result

参考答案:

def accumulate(combiner, base, n, term):
    total, k = base, 1
    while k <= n:
        total, k = combiner(total, term(k)), k + 1
    return total

# Recursive solution
def accumulate2(combiner, base, n, term):
    if n == 0:
        return base
    return combiner(term(n), accumulate2(combiner, base, n-1, term))

# Alternative recursive solution using base to keep track of total
def accumulate3(combiner, base, n, term):
    if n == 0:
        return base
    return accumulate3(combiner, combiner(base, term(n)), n-1, term)

NOTE

在Q2基础上抽象。

Q4: Make Repeater

Implement a function make_repeater so that make_repeater(f, n)(x) returns f(f(...f(x)...)), where f is applied n times. That is, make_repeater(f, n) returns another function that can then be applied to another argument. For example, make_repeater(square, 3)(42) evaluates to square(square(square(42))). See if you can figure out a reasonable function to return for that case. You may use either loops or recursion in your implementation.

def make_repeater(f, n):
    """Return 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, 4)(5) # square(square(square(square(5))))
    152587890625
    >>> make_repeater(square, 0)(5) # Yes, it makes sense to apply the function zero times! 
    5
    """
    "*** YOUR CODE HERE ***"
    
def compose1(f, g):
    """Return a function h, such that h(x) = f(g(x))."""
    def h(x):
        return f(g(x))
    return h

我的解答:

    # method 1
    # def f1(n, k):
    #     n -= 1
    #     if n > 0:
    #         return f(f1(n, k))
    #     else:
    #         return f(k)
    # if n:
    #     return lambda k: f1(n, k)
    # else:
    #     return lambda k: k  # deal with the case n == 0

    # method 2
    # return lambda k: accumulate(compose1, k, n - 1, lambda a: f)  # deprecated
    return accumulate(compose1, identity, n, lambda a: f) 

参考答案

def make_repeater(f, n):
    g = identity
    while n > 0:
        g = compose1(f, g)
        n = n - 1
    return g

# Alternative solutions
def make_repeater2(f, n):
    def h(x):
        k = 0
        while k < n:
            x, k = f(x), k + 1
        return x
    return h

def make_repeater3(f, n):
    if n == 0:
        return lambda x: x
    return lambda x: f(make_repeater3(f, n - 1)(x))

def make_repeater4(f, n):
    if n == 0:
        return lambda x: x
    return compose1(f, make_repeater4(f, n - 1))

def make_repeater5(f, n):
    return accumulate(compose1, lambda x: x, n, lambda k: f)

NOTE

参考答案的法1和法2较为直观;法3使用递归实现,原理为:make_repeater(f, n)(x) = f(make_repeater(f, n-1)(x))
法5使用1行return语句实现。accumulate函数定义:

def accumulate(combiner, base, n, term):
    total, k = base, 1
    while k <= n:
        total, k = combiner(total, term(k)), k + 1
    return total

进入accumulate函数,名称与对象的对应关系为:

名称 对象
combiner func compose1(f, g)
base lambda x: x
n n
term lambda k: f
total lambda x: x
k 1

在Q3中,accumulate函数返回计算结果,而Q4要求返回函数,所以给f套壳:lambda k: f,配合compose1实现函数的返回。
可以将accumulate函数改写为:

# def accumulate(combiner, base, n, term):
	total, k = identity, 1
	while k <= n:
		total, k = compose1(total, f), 1
	return total

注意对于compose1(f, g),当fg不变或者为identity时,两个参数可以交换位置。因此上述代码和参考答案法1是完全相同的。

你可能感兴趣的:(Python)