这里,我给出个人对于上一篇文章中题目的一种解答。
首先,对于 one
two
这两个函数的定义,这个对于很多人来说,应该是没有什么难度的。根据successor
的定义,我们可以得到下面两个函数的定义
def zero(f):
return lambda x: x
def successor(n):
return lambda f: lambda x: f(n(f)(x))
def one(f):
"""Church numeral 1: same as successor(zero)"""
"*** YOUR CODE HERE ***"
return lambda x: f(zero(f)(x))
def two(f):
"""Church numeral 2: same as successor(successor(zero))"""
"*** YOUR CODE HERE ***"
return lambda x: f(one(f)(x))
three = successor(two)
好了,接下来是真正的实验——church_to_int(n)
函数。这里,有两种可能: 从 0 到 n 和 从 n 到 0。两种方法我都试过,不过从 0 到n 貌似出不来结果,如果你的解答是从 0 到 n 算出来的,麻烦告诉我一声。
那么,接下来让我们从two
到zero
进行分析(当然,你可以选择直接分析successor
,如果你没被他的定义搞晕的话)。
首先我们应该明白,two(f)
返回的是一个函数,该函数有一个formal parameter x
。然后,让我们更进一步,假定有这么一个变量x
存在,然后two(f)(x) == Two
。同样的,one(f)(x) == One, zero(f)(x) == Zero
(注意我这里用了大写)。
那么将有
two(f)(x) -> f(One)
one(f)(x) -> f(Zero)
到了这里,是不是觉得,好像发现了什么呢?至少,很有规律,不是吗?
对于每一个 Church number function,我们都将比他小1
的函数的返回值,作为实参,再调用了一次f
函数。也就是说,对于一个数值为n
的函数,f
会被调用n
次(一般情况下是如此,后面我们将挑战这一权威)。说到这里,答案其实已经浮出了水面,如果你没有忘记题目额外给出的一个increment
函数的话。
def increment(x):
return x + 1
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 ***"
return n(increment)(0)
是不是很惊讶,答案居然是如此的简洁(老实说,个人写出这个一行代码时,也是觉得挺震撼的,题目的设计者真是个天才)。每次调用increment
的时候,都会 +1
,我们执行了increment
n
次,也就得到了我们想要的int
值。
对于add_church
函数,直觉上,想要从 m
和n
而得到m+n
,我们只需要简单地从n
使用successor
递增m
步即可(当然,你也可以先判断哪一个比较小,这里暂时忽略这个问题)。于是,便有了下面这个解答:
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 >>> church_to_int(add_church(zero, three)) 3 """
"*** YOUR CODE HERE ***"
num_m = church_to_int(m)
while num_m > 0:
n = successor(n)
num_m -= 1
return n
# there is a better solution below
不知道你对这个答案满意度如何,我呢,总觉得有些别扭。这里我们居然用了5 行代码!5这个数字也许不是很大,但比起上一个问题中的1行代码,确实有点太多了。不过,这里我先放一放,后面我们再来讨论这个问题。
好啦,先放下add_church
这么一个疙瘩,我们来看看mul_church
。
在告诉你答案前,我觉得,还是先来复习一下小学乘法:
2×3 表示 2 个 3 或 3 个 2
不管怎么样,还是先急着这个吧。
在前面我们实现church_to_int(f)
时,我们在每次调用f
时执行了+1
操作,最后得到了church 函数对应的 整数值。这里,我们可以如法炮制。不管,现在每次增加的是 m
而不是1
(这里判断m, n
那个比较小没有任何意义)。沿着这个思路,我们可能会写出:
def mul_church(m, n):
num_m = church_to_int(m)
add_m = lambda x: x + num_m
return n(add_m)(0)
很遗憾,这个是错的。我们需要的是一个 church 函数,而不是一个数字。不过,这是一个很好的开端。我们需要返回一个高阶函数,该函数接收一个函数作为实参且church_to_int
利用该传入的实参计算对应的整数值。
接下来需要一个小小的跳跃。以我们church_to_int
的实现来看,难道你传一个increment
给我,我就得乖乖用他来一步一步加1
吗?!为什么我们不能自作主张,一步加m
呢?!(还记得上面说过的吗? m×n 就是 n 个 m 相加)根据这个思想,就有了下面这个解答,从解决问题的思想上,和上面的解答其实是一样的
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 >>> church_to_int(successor(mul_church(three, four))) 13 """
"*** YOUR CODE HERE ***"
num_m = church_to_int(m)
add_m = lambda x: x + num_m
return lambda f: n(add_m)
# or just
# return lambda f: n(lambda x: x + num_m)
现在,让我们回到前面的 add_church(m, n)
。上面给出的答案是,从 n
开始,一步一步,得到了 m + n
。但是,不管怎么说,这种做法总是不够优雅。得益于上面对mul_church(m, n)
的思考,有这么一个想法,我们能够一步就将m
加到n
呢?答案是,可以。
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 >>> church_to_int(add_church(zero, three)) 3 """
"*** YOUR CODE HERE ***"
num_m = church_to_int(m)
return lambda f: lambda x: num_m + n(f)(x)
虽然较之前面的答案更为的复杂,但个人觉得,也还算优雅。这里需要注意的一点是,lambda f: lambda x: n(f)(x)
就等价于 n
。当我们计算其对应的整数值时,加上了额外的 num_m
,于是也就得到了 m+n
对应的整数值。
是不是觉得很兴奋?非常的有趣,不是吗?到了这里,综合我的解说和你的理解,我相信,你有能力做出最后的 pow_church(m, n)
。来吧,尝试一下,不然我可就只能独享这大餐了。