CS61A Homework: Church Numerals

Church Numerals


Nagging

南大的 SICP 其實是 Berkeley CS61A 的 clone ,因此我有幸作到了這個 Homework02。python

此外要感謝選課系統,讓我一個工科學生也能有幸享受世界一流大學的 CS 課程。redis

今天是 SICP 的 Lecture 5 ,這些 higher-order function 的內容徹底是個人知識盲區。可見我以爲本身稍微有點的那些水平充其量也就是百川灌河罷了。數據結構

南大給的講義上說:ide

This section is out of scope for our course, so the problems below is optional.
That is, the problems in this section don't count for your final score and don't have any deadline.
Do it at any time if you want an extra challenge or some practice with high order function and abstraction!函數

既然都不計成績,我以爲代碼應該是能放到網上的。所以有了這篇文章。this

P.s. 問過助教老師了,是能夠發上網的spa


Homework

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:code

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 successor(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.orm

##########################
# Just for fun Questions #
##########################
HW_SOURCE_FILE = 'lab02.py'

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

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 ***"

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

很厲害的題目。我是第一次以這種角度思考問題,這種體驗使人很興奮。three

首先考慮補完 onetwo 兩個函數。

按照 zerosuccessor 的定義,咱們很容易就能不動腦子地寫出代碼。固然,題目本來應該並不是這個意思。

考慮稍微畫兩下,容易獲得這樣的代碼:

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

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

successor 的定義,發現數字 \(N\) 對應的函數 \(F_N(f)\) 的定義應當爲:

\[F_N(f) = f(F_{N-1}(f)) = \dots = \underbrace{f(f(f(\dots(f}_{N個})))) \]

也就是說,這裏的數字 \(N\) 實際上表示嵌套的 \(f\) 個數。很容易用概括法證實。


接下來考慮 church_to_int 的實現。

容易發現 church_to_int 本質上就是計數嵌套的 \(f\) 有多少個。要數數,固然就是要逐層把函數嵌套走一遍了,每走一層就給計數變量加一。那麼考慮將 \(f\) 設置爲自增函數 increment 。這樣,傳入的參數值就是計數變量的初值,函數的返回值就是終值了。

def church_to_int(n):
    return n(increment)(0)

而後是 add_church 。要實現這個函數固然能夠一個循環跑下來。可是,那不夠美。

這三個單行函數寫下來,你還好意思用長篇大論去實現某個小功能嗎?顯然否。

考慮計算 \(m + n\) ,也就是把 \(m + n\)\(f\) 套在一塊兒。而咱們知道 \(F_m\) 能夠實現 \(m\) 次嵌套, \(F_n\) 能夠實現 \(n\) 次嵌套,咱們在 \(F_n\) 外面套一個 \(F_m\) 便可,那麼就有:

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

接下來考慮實現 mul_church ,計算 \(n \times m\) 也就是 \(m\)\(n\) 相加,

換言之,要把 \(F_n\) 本身套本身套上 \(m\) 次,代碼很是簡單:

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

最後考慮 pow_church ,這實際上是最富技巧性的一個,或許也是最簡單的一個。

\(F_m(f)\) 的功用是將 \(m\)\(f\) 嵌套起來,那麼 \(F_n(F_m(f))\) 的功用也就是將 \(n\)\(F_m\) 嵌套起來,即

\[\begin{align*} F_n(F_m(f)) &= \underbrace{F_m(F_m(F_m(\dots(F_m}_{n個})))) \\ &= \overbrace{\underbrace{f(f(f(\dots(f(\underbrace{f(f(f(\dots(f(\dots \underbrace{f(f(f(\dots \dots \dots(f}_{m個})))))}_{m個})))))}_{m個}}^{n個})))) \end{align*} \]

這正是乘方的定義。

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

至此作完了。

其實寫博客的時候就會發現許多事情想明白了卻說不明白,實在是我水平有限。

作完這樣一道題目讓人充滿了成就感。從 花一小時寫出來的五六行代碼 與 從幾小時碼出的幾百行數據結構 得到的成就感是不同的。相較而言,前者彷佛更具備一種茅塞頓開的快感。

相關文章
相關標籤/搜索