CS 61A: Structure and Interpretation of Computer Programs 课程官网
Homework 2: Higher Order Functions 作业链接 参考答案
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
Implement the
make_adder
function, which takes in a numbern
and returns a function that takes in an another numberk
and returnsn + k
. Your solution must consist of a singlereturn
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
使用lambda表达式定义匿名函数,冒号前为匿名函数的形式参数(输入),冒号后为返回值(输出)。
本题代码将add(n, k)
转化为make_adder(n)(k)
,这种变形称为柯里化。
柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
百度百科
make_adder(n)
函数接受参数n
,并返回接受参数k
的匿名函数,匿名函数返回n + k
的结果。
柯里化在JS中用得比较多。
相关资料:
简述几个非常有用的柯里化函数使用场景
为什么柯里化有用(下午茶译文赏析)
The
summation(n, term)
function from the higher-order functions lecture adds upterm(1) + ... + term(n)
. Write a similar function calledproduct
that returnsterm(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
product(n, term)
的形参term
接收有一个参数的函数,也就是将函数作为参数输入到product()
。这样就将“求term(1)-term(n)乘积”这个动作抽象成了函数product()
,不管是求二次方的乘积、三次方的乘积,都可以直接调用product()
,只需要将term
修改为对应的计算函数。
Let’s take a look at how
summation
andproduct
are instances of a more general function calledaccumulate
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)
在Q2基础上抽象。
Implement a function
make_repeater
so thatmake_repeater(f, n)(x)
returnsf(f(...f(x)...))
, wheref
is appliedn
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 tosquare(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)
参考答案的法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)
,当f
和g
不变或者为identity
时,两个参数可以交换位置。因此上述代码和参考答案法1是完全相同的。