《同构——编程中的数学》笔记

第1章-数字

1.4-自然数的结构

\[f(n) = foldn(c,h)(n) \]

我对这个符号的理解是,以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)\)

\[(+m) = foldn(m, succ) \]

n (+m)
0 m
1 succ(m) = 1+m
2 succ(succ(m)) = 2+m
3 succ(succ(succ(m))) = 3+m
... ...

定义操作 \((· \ m)\)

\[(\cdot \ m) = foldn(0, (+m)) \]

n (· m)
0 0
1 0 + m = m
2 0 + m + m = 2m
3 0 + m + m + m = 3m
... ...

定义操作 \(m^{( \ )}\)

\[m^{( \ )} = foldn(1, (\cdot \ 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),另外,还需要定义取数操作:

\[\begin{align} 1st(a,b) = a \nonumber \\ 2nd(a,b)= b \nonumber \end{align} \]

首先定义累加acc:

\[ acc = 2nd \cdot foldn(c,h) \]

其中 \(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
... ...

定义阶乘!:

\[ ! = 2nd \cdot foldn(c,h) \]

其中 \(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]

同构于自然数加法,定义列表的连接运算:

\[\begin{align} nil +\hspace{-3.3mm}+ \ y = y \nonumber & \\ cons(a, x) +\hspace{-3.3mm}+ \ y = cons(a, x +\hspace{-3.3mm}+ \ y) & \nonumber \end{align} \]

满足结合律,但不满足交换律。

与自然数一样,定义链表的抽象操作。

\[\begin{align} &f(nil) = c \nonumber \\ &f(a:x)) = h(a, f(x)) \nonumber \end{align} \]

(这个记号比较难看懂,先接着看下文)

\(f = foldr(c, h)\) ,则

\[\begin{align} &foldr(c, h, nil) = c \nonumber \\ &foldr(c, h, a:x) = h(a, foldr(c, h, x)) \nonumber \end{align} \]

取名叫foldr是为了表征这个操作是自右向左的,例如:

\[\begin{align} foldr(c, h, \ m:n:nil)& = h(m, \ foldr(c, h, n:nil) \ ) \nonumber \\ & = h(m, \ h(n, \ foldr(c, h, nil)\ ) \ ) \nonumber \\ & = h(m, h(n, c)) \nonumber \end{align} \]

(最后一个等号是因为,对于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}+ )\) ,得到列表的“乘法”操作,即将一个“列表的列表”全部连接起来。
    例如:

\[\begin{align} concat([\ [1,1], [2, 3, 5], [8] \ ] )& = [1, 1] +\hspace{-3.3mm}+ foldr(nil, +\hspace{-2.3mm}+ , [2,3,5]:[8]) \nonumber \\ & = [1, 1] +\hspace{-3.3mm}+ [2,3,5] +\hspace{-3.3mm}+ foldr(nil, +\hspace{-2.3mm}+, [8])\nonumber \\ & = [1, 1] +\hspace{-3.3mm}+ [2,3,5] +\hspace{-3.3mm}+ [8] +\hspace{-3.3mm}+ foldr(nil, +\hspace{-2.3mm}+, nil)\nonumber \\ & = [1, 1, 2, 3, 5, 8] \nonumber \end{align} \]

选择(过滤)和逐一映射

选择(过滤)

条件表达式: \((p \rightarrow f, g)\) 表示如果p(x)成立,则结果为f(x),否则结果为g(x)。

这样就可以定义过滤操作:

\[filter(p) = foldr(nil, (p \cdot 1st \rightarrow cons, 2nd)) \]

(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

posted @ 2023-11-11 22:46  码鸽  阅读(104)  评论(0编辑  收藏  举报