---
tags: cs61a
---
# 2019 CS61A HW2 Q4: Church Numerals (python)
6/20
當初看到這題第一眼時,整個人直接傻掉,連題目的意思都看不太懂...
題目網址: https://inst.eecs.berkeley.edu/~cs61a/fa19/hw/hw02/
Church Numerals 是美國數學家與邏輯學家 Alonzo Church 發明的數字系統,他發明的這套數字系統不需要倚賴我們原來的數字123, 它純粹是以邏輯建構出來的,而它可以進行加法、乘法、Boolean 運算等數學運算。
這題是讓我們訓練對 Higher Order Fuction 的感覺,意思就是函數的函數的函數..., 所以在做的時候頭腦很容易就打結。
---
## 題目
題目提供 Church Numerals 的基礎元素,0 以及一個能夠找到任何數字下一個數字的函數。
```
zero(f):
return lambda x: x
def successor(n):
return lambda f: lambda x: f(n(f)(x))
```
在這個系統 zero 就相當於是數字0,它是一個函數會回傳一個函數,也可以把它寫成
```
zero = lambda f: lambda x: x
```
而 successor 能夠找到下一個數字
```
one = successor(zero)
two = successor(one)
```
## 計算元素 one 與 two (不可以用到 successor 函數)
### 計算one
將zero帶入successor的參數裡
successor(zero) 會 return -> lambda f: lambda x: f(zero(f)(x)) - - -(1)
而 zero(f) 會 return -> lambda x: x - - -(2)
將(2)帶回(1):
lambda f: lambda x: f(<font color="#008000">(lambda x: x)</font><font color="#f00">(x)</font>)
化簡一下
```
one = lambda f: lambda x: f(x)
```
也可以寫成
```
def one(f):
return lambda x: f(x)
```
### 計算two
將one帶入successor的參數裡
successor(one) 會 return -> lambda f: lambda x: f(one(f)(x)) - - -(1)
同上面計算one
將one(f)帶入(1):
lambda f: lambda x: f(<font color="#008000">(lambda x: f(x))</font><font color="#f00">(x)</font>)
化簡一下
```
two = lambda f: lambda x: f(f(x))
```
也可以寫成
```
def two(f):
return lambda x: f(f(x))
```
至此已能稍微看出一些規律,首先這個數字系統的數字都是會回傳函數的函數,其參數f也是一個函數,也就是開頭都是lambda f: lambda x:,而它回傳的函數的回傳值為f(x)的組合,one是f(x)、two是ff(x),可以想像three是fff(x)。
## 設計 Church Numerals 與 一般數字的轉換函數 Church_to_int
為了方便觀察運算結果,先設計Church_to_int
目標:
Church_to_int(zero) -> 0
Church_to_int(one) -> 1
Church_to_int(two) -> 2
可以先想到的是 one、two 回傳的函數 lambda x: 回傳的 f(x) 的個數和它所代表的一般數字是一樣的,one 就回傳一個、two 就兩個,所以必須善用這個 f 函數。
將 f 函數設計成一個能夠計數的函數
```
f = lambda x: x+1
#f(0) = 1
#f(1) = 2
```
將 f 放進 Church_to_in 裡
```
def Church_to_int(n):
f = lambda x: x+1
return n(f)(0)
#Church_to_int(zero) -> zero(f)(0) = 0
#Church_to_int(one) -> one(f)(0) = 1 (跑一次f(x))
#Church_to_int(two) -> two(f)(0) = 2 (跑兩次f(x))
```
也可以寫成
```
f = lambda x: x+1
Church_to_int = lambda n: n(f)(0)
```
---
以下運算,加法、乘法和乘冪皆不可以用到 successor 函數
## 設計加法運算 add_Church(m, n)
首先先想像一下在這裡的加法運算應該長甚麼樣子
目標:
add_Church(two, three) -> five
也就是ff(x) + fff(x) -> fffff(x)
可以看出要做的事情是把two當中的x帶入three回傳的函數的回傳值fff(x)
把這件事拆成兩個部分:
1. 把two當中的x代入:
two(f)(<font color="#f00">x</font>)
2. three回傳的函數的回傳值fff(x):
three(f)(x)
合起來就是 two(f)(three(f)(x)),此時的值為 fffff(x),還不是我們要的 five,因為 five 是一個回傳函數的函數,開頭應該 lambda f: lambda x:
整合一下
```
def add_Church(m, n):
return lambda f: lambda x: m(f)(n(f)(x))
```
也可以寫成
```
add_Church(m, n) = lambda m, n: lambda f: lambda x: m(f)(n(f)(x))
```
## 設計乘法運算 mul_Church(m, n)
乘法運算就比加法運算難一些
目標:
mul_Church(two, three) -> six
也就是ff(x) * fff(x) -> ffffff(x)
我們知道2 * 3能夠被看成是有2份的3,所以往這個方向去想,如果 two 的 f 函式的功能就是製造出fff(x),也就是兩份的 three 那乘法就被實現出來了。
把這件事拆成兩個部分:
1. 製造 three 回傳的函數的回傳值 fff(x):
three(f) = lambda x: fff(x)
2. 代入 two 當中的 f 函式:
two(<font color="#008000">f</font>)(x)
合起來就是 two(three(f))(x)
two(three(f))(x) = three(three(f)(x)) = ffffff(x)
此時的值為 ffffff(x),還不是我們要的 six,因為 six 是一個回傳函數的函數,開頭應該lambda f: lambda x:
整合一下
```
def mul_Church(m, n):
return lambda f: lambda x: m(n(f))(x)
```
也可以寫成
```
mul_Church(m, n) = lambda m, n: lambda f: lambda x: m(n(f))(x)
```
注意一點,這裡的 lambda f: lambda x: m(n(f)(x))(x) 可以直接寫成
lambda f: m(n(f)) 就可以了,因為 m(n(f)) 本身的回傳值就是 lambda x: ffffff(x)
## 設計乘冪運算(次方運算) pow_church(m, n)
目標:
pow_church(two, three) -> $2^3 = 8$
次方運算跟乘法運算的關係,就好比乘法運算跟加法運算的關係
乘法是連續的加法 -> $2 * 3 = 2 + 2 + 2$
次方是連續的乘法 -> $2^3 = 2 * 2 * 2$
所以我們可以往這個方向找線索
為了要讓3個2相乘,我們現在需要使 three 當中的 fff(x) 的 f 函式之間為相乘2的關係
以下進行分析:
* 使 three 當中的 fff(x) 的 f 函式之間為相乘2的關係:
為了防止搞混,以下先將 three 的 f(x) 簡稱 f3
要使 f3(f3(x)) 之間為乘2的關係,我們必須要讓 f3 成為一個會執行 x 兩次的一個函式,
此時的 x 為一個函數
f3(<font color="#f00">x</font>) 會執行 <font color="#f00">x</font> 兩次
f3(<font color="#f00">f3(x)</font>) 會執行 <font color="#f00">f3(x)</font> 兩次,也就是執行 x 四次
所以 f3 是一個能夠執行其參數 (可能為某個函數 f ) 兩次的一個函數
f3 = lambda f: lambda x: f(f(x))
有沒有發現這個東西就是 two
所以 f3(f3(f3((x)) 其實就是 two(two(two(x)))
這時我們就發現 two 設計成參數為函數並且回傳一個函數的目的之一,就是讓 two 能夠在身為數 字的同時,也能當作運算來使用
整理一下把 two 代入 three(f)
three = lambda f: lambda x: fff(x)
three(two) = lambda x: two(two(two(x)))
要注意的是此時原先的 x 變成了 f 的功用,因為 x 會代入 two(x),我們替換一下寫法,
three(two) = lambda f: <font color="#1338BE">two(</font><font color="#008000">two(</font><font color="#f00">two(f)</font><font color="#008000">)</font><font color="#1338BE">)</font> = lambda f: lambda x: ffffffff(x)
也就是 eight
eight(f)(x) = <font color="#1338BE">two(</font><font color="#008000">two(</font><font color="#f00">two(f)</font><font color="#008000">)</font><font color="#1338BE">)</font>(x) = ffffffff(x)

```
# m的n次方
def pow_church(m, n):
return n(m)
```
也可以寫成
```
pow_church(m, n) = lambda f: n(m)
```