wys的个人博客

你有很多事放不下?做人要潇洒一点~

0%

cs61a难题摘录

CS61A难题摘录

HW02

problem

The logician Alonzo Church invented a system of representing non-negative integers entirely using functions. The purpose was to show that functions are sufficient to describe all of number theory: if we have functions, we do not need to assume that numbers exist, but instead we can invent them.

Your goal in this problem is to rediscover this representation known as Church numerals. Here are the definitions of zero, as well as a function that returns one more than its argument:

1
2
3
4
5
def zero(f):
return lambda x: x

def successor(n):
return lambda f: lambda x: f(n(f)(x))

First, define functions one and two such that they have the same behavior as successor(zero) and successsor(successor(zero)) respectively, but do not call successor in your implementation.

Next, implement a function church_to_int that converts a church numeral argument to a regular Python integer.

Finally, implement functions add_church, mul_church, and pow_church that perform addition, multiplication, and exponentiation on church numerals.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
def one(f):
"""Church numeral 1: same as successor(zero)"""
"*** YOUR CODE HERE ***"

def two(f):
"""Church numeral 2: same as successor(successor(zero))"""
"*** YOUR CODE HERE ***"

three = successor(two)

def church_to_int(n):
"""Convert the Church numeral n to a Python integer.

>>> church_to_int(zero)
0
>>> church_to_int(one)
1
>>> church_to_int(two)
2
>>> church_to_int(three)
3
"""
"*** YOUR CODE HERE ***"

def add_church(m, n):
"""Return the Church numeral for m + n, for Church numerals m and n.

>>> church_to_int(add_church(two, three))
5
"""
"*** YOUR CODE HERE ***"

def mul_church(m, n):
"""Return the Church numeral for m * n, for Church numerals m and n.

>>> four = successor(three)
>>> church_to_int(mul_church(two, three))
6
>>> church_to_int(mul_church(three, four))
12
"""
"*** YOUR CODE HERE ***"

def pow_church(m, n):
"""Return the Church numeral m ** n, for Church numerals m and n.

>>> church_to_int(pow_church(two, three))
8
>>> church_to_int(pow_church(three, two))
9
"""
"*** YOUR CODE HERE ***"

solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def zero(f):
return lambda x: x

def successor(n):
return lambda f: lambda x: f(n(f)(x))

def one(f):
return lambda x:f(x)

def two(f):
return lambda x:f(f(x))

three = successor(two)

def church_to_int(n):
return n(lambda x:x+1)(0)

def add_church(m, n):
return lambda f:lambda x: m(f)(n(f)(x))

def mul_church(m, n):
return lambda f: m(n(f))

def pow_church(m, n):
return n(m)

pow_church 难以理解

HW03 Q1

problem

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def composer(func=lambda x: x):
"""
Returns two functions -
one holding the composed function so far, and another
that can create further composed problems.
>>> add_one = lambda x: x + 1
>>> mul_two = lambda x: x * 2
>>> f, func_adder = composer()
>>> f1, func_adder = func_adder(add_one)
>>> f1(3)
4
>>> f2, func_adder = func_adder(mul_two)
>>> f2(3) # should be 1 + (2*3) = 7
7
>>> f3, func_adder = func_adder(add_one)
>>> f3(3) # should be 1 + (2 * (3 + 1)) = 9
9
"""
def func_adder(g):
"*** YOUR CODE HERE ***"
return func, func_adder

solution

1
2
3
4
5
6
def composer(func=lambda x: x):
def func_adder(g):
def fun(x):
return func(g(x))
return composer(fun)
return func, func_adder

HW03 Q6

problem

The recursive factorial function can be written as a single expression by using a conditional expression.

1
2
3
>>> fact = lambda n: 1 if n == 1 else mul(n, fact(sub(n, 1)))
>>> fact(5)
120

However, this implementation relies on the fact (no pun intended) that fact has a name, to which we refer in the body of fact. To write a recursive function, we have always given it a name using a def or assignment statement so that we can refer to the function within its own body. In this question, your job is to define fact recursively without giving it a name!

Write an expression that computes n factorial using only call expressions, conditional expressions, and lambda expressions (no assignment or def statements). Note in particular that you are not allowed to use make_anonymous_factorial in your return expression. The sub and mul functions from the operator module are the only built-in functions required to solve this problem:

1
2
3
4
5
6
7
8
9
10
11
12
13
from operator import sub, mul

def make_anonymous_factorial():
"""Return the value of an expression that computes factorial.

>>> make_anonymous_factorial()(5)
120
>>> from construct_check import check
>>> # ban any assignments or recursion
>>> check(HW_SOURCE_FILE, 'make_anonymous_factorial', ['Assign', 'AugAssign', 'FunctionDef', 'Recursion'])
True
"""
return 'YOUR_EXPRESSION_HERE'

solution

1
2
3
4
5
6
7
8
9
from operator import sub, mul

def make_anonymous_factorial():
return (lambda f: lambda k: f(f, k))(lambda f, k: k if k == 1 else mul(k, f(f, sub(k, 1))))
# Alternate solution1:
# return (lambda f: f(f))(lambda f: lambda x: 1 if x == 0 else x * f(f)(x - 1))
# Alternate solution2:
# from functools import reduce
# return lambda n:reduce(mul, range(1, n + 1))

solution2 感觉比较取巧,前两种解法又实在难以理解。。

Lab07

Q6: Number of Trees

problem

How many different possible full binary tree (each node has 2 branches or 0, but never 1) structures exist that have exactly n leaves?

For those interested in combinatorics, this problem does have a closed form solution):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def num_trees(n):
"""How many full binary trees have exactly n leaves? E.g.,

1 2 3 3 ...
* * * *
/ \ / \ / \
* * * * * *
/ \ / \
* * * *

>>> num_trees(1)
1
>>> num_trees(2)
1
>>> num_trees(3)
2
>>> num_trees(8)
429

"""
if ____________________:
return _______________
return _______________

solution

答案是卡特兰数, 所以我们要实现的其实是卡特兰数的递归写法. 至于为什么是卡特兰数我也想不大明白, 比较能接受的解释是, 完全二叉树的左右子树肯定也是完全二叉树, 假设左子树有 1 个叶子结点, 右子树就有 n - 1 个叶子结点, 那么此时就有 f(1) * f(n - 1) 种可能, 类似的, 如果左子树有 2 个叶子结点, 那就是 f(2) * f(n - 2), 这样累加起来就是卡特兰数.

ps: 这里的完全二叉树不是严格意义上的, 确切来说这里指的是所有节点的度只能为 0 或者 2 的树

1
2
3
4
5
6
7
8
9
10
def num_trees(n):
"""Returns the number of unique full binary trees with exactly n leaves. E.g.,
"""
if n == 1 or n == 2:
return 1
# catalan number
ans = 0
for i in range(1, n):
ans += num_trees(i) * num_trees(n - i)
return ans

化简后的答案

1
2
3
if n == 1 or n == 2:
return 1
return num_trees(n-1)*2*(2*n-3)//n