《同构——编程中的数学》笔记
第1章-数字
1.4-自然数的结构
我对这个符号的理解是,以c为初始值,对初始值进行n次的h操作。
许多东西都可以由这个记号定义,例如自然数序列:
取 \(n = 0, h = succ\) (succ表示后继),则
n | f |
---|---|
0 | 0 |
1 | succ(0) = 1 |
2 | succ(succ(0)) = 2 |
3 | succ(succ(succ(0))) = 3 |
... | ... |
而对于这个记号,更重要的是它描述了与自然数同构的某种事物。
比如定义操作 \((+m)\) :
则
n | (+m) |
---|---|
0 | m |
1 | succ(m) = 1+m |
2 | succ(succ(m)) = 2+m |
3 | succ(succ(succ(m))) = 3+m |
... | ... |
定义操作 \((· \ m)\) :
则
n | (· m) |
---|---|
0 | 0 |
1 | 0 + m = m |
2 | 0 + m + m = 2m |
3 | 0 + m + m + m = 3m |
... | ... |
定义操作 \(m^{( \ )}\) :
则
n | \(m^{( \ )}\) |
---|---|
0 | 1 |
1 | 1 · m = m |
2 | 1 · m · m = m² |
3 | 1 · m · m · m = m³ |
... | ... |
对于累加和阶乘则稍微复杂一些,需要引入二元数(a,b),并且h也是对二元数的操作h(a,b),另外,还需要定义取数操作:
首先定义累加acc:
其中 \(c = (0,0)\) 、\(h(a,b) = (a', a'+b)\) ,a'表示succ(a),即a的后继。
则
n | (a,b) | acc |
---|---|---|
0 | (0, 0) | 0 |
1 | (0', 0' + 0) = (1, 1) | 1 |
2 | (1', 1' + 1) = (2, 3) | 3 |
3 | (2', 2' + 3) = (3, 6) | 6 |
... | ... |
定义阶乘!:
其中 \(c = (0,1)\) 、\(h(a,b) = (a', a' \cdot b)\)
则
n | (a, b) | ! |
---|---|---|
0 | (0, 1) | 1 |
1 | (0', 0' · 1) = (1, 1) | 1 |
2 | (1', 1' · 1) = (2, 2) | 2 |
3 | (2', 2' · 2) = (3, 6) | 6 |
4 | (3', 3' · 6) = (4, 24) | 24 |
... | ... |
1.5-自然数的同构
链表与自然数也是同构的。
定义空列表为nil;
定义链接操作为cons(同构于自然数的后继):
例如:
- cons(3, nil) = 3 -> nil
- cons(9, cons(3, nil) ) = 9 -> 3 -> nil
cons简记为 : (冒号)
例如:
- 3 : nil = [3]
- 9 : [3] = [9, 3]
同构于自然数加法,定义列表的连接运算:
满足结合律,但不满足交换律。
与自然数一样,定义链表的抽象操作。
(这个记号比较难看懂,先接着看下文)
令 \(f = foldr(c, h)\) ,则
取名叫foldr是为了表征这个操作是自右向左的,例如:
(最后一个等号是因为,对于nil的操作是单独定义的: \(f(nil) = c \ 即foldr(c, h, nil) = c\) )
从这个结果来看,h先作用于nil,然后再是n和m,因此是从右向左。
一些例子:
-
定义length = foldr(0, h),h(a, n) = n + 1,得到计算列表长度的操作
-
定义 \((+\hspace{-3.3mm}+ y) = foldr(y,cons)\)得到链接操作
-
定义 \(concat = foldr(nil, +\hspace{-2.3mm}+ )\) ,得到列表的“乘法”操作,即将一个“列表的列表”全部连接起来。
例如:
选择(过滤)和逐一映射
选择(过滤)
条件表达式: \((p \rightarrow f, g)\) 表示如果p(x)成立,则结果为f(x),否则结果为g(x)。
这样就可以定义过滤操作:
(p · 1st表示先提取第一个参数,再用这个参数进行条件p判断)
例如:filter(even)可以选择出偶数。
例如给filter(even)传入参数[1, 4, 9, 16, 25]
即filter(even, [1, 4, 9, 16, 25])
filter(even) = foldr(nil, (even · 1st -> cons, 2nd))
先记h为(even · 1st -> cons, 2nd),则
filter(even) = foldr(nil, h)
那么
filter(even, h, [1, 4, 9, 16, 25]) = h(1, h(4, h(9, h(16, foldr(nil, h, nil)))))
= h(1, h(4, h(9, h(16, nil))))
= h(1, h(4, h(9, even· 1st -> cons, 2nd (16,nil)))) ——将h展开
= h(1, h(4, h(9, even· 1st -> cons, 2nd (16,nil))))
= h(1, h(4, h(9, 16)))
= h(1, h(4, even· 1st -> cons, 2nd (9, 16)))
= h(1, h(4, 16))
= h(1, [4,16])
= [4, 16]
逐一映射
逐一映射就是map(f, [x1, x2, ..., xn]) = [f(x1), f(x2), ..., f(xn)]
可以用foldr定义为:
map( f ) = foldr( nil, h )
h(x, c) = cons( f(x), c )
例如:map( f, [1, 3, 5] )
= foldr( nil, h, [1, 3, 5])
= h( 1, h(3, h(5, foldr(nil, h, nil))))
= h( 1, h(3, h(5, nil)))
= h( 1, h(3, f(5) : nil))
= h( 1, f(3) : f(5))
= f(1) : f(3) : f(5)
= [f(1), f(3), f(5)]
求列表的长度也可以用逐一映射实现:
len = sum · map( one )
one( x ) = 1
sum · map ( one, [3, 6] )
= sum · foldr ( nil, h, [3, 6] )
= sum · h ( 3, h ( 6, foldr ( nil, h, nil ) )
= sum · h ( 3, h ( 6, nil )
= sum · h ( 3, cons ( one( 6 ), nil ) )
= sum · h ( 3, [1] )
= sum · cons ( one ( 3 ), [1] )
= sum ( [1, 1] )
= 2