一、前言
这片文章并不打算让你对数学感兴趣并引导你去深入精通。
如果看完后你深入数学无法自拔,那么我很抱歉,虽然数学是很迷人,但我希望你看过这片文章后可以借助对数学的理解更好的去理解函数式编程。
如果你当成一篇故事来看,能让你稍微对数学敏感一点,那是我非常乐意看到的,毕竟生活离不开数学,不信你看双11
二、函数
函数即映射
2.1 函数的历史
函数是数学的一个基本概念,其概念的形成有较长的历史过程。在古代数学中函数依赖的思想没有明显地表达出来,而且不是独立的研究对象。函数概念的雏形在中世纪开始出现于学者的著作中。
但仅仅在17 世纪,首先在费马、笛卡儿、牛顿、莱布尼茨的工作中,函数才作为一个独立的概念逐渐定形。函数一词最先出现在莱布尼茨的著作中,用以表示随曲线上的点变动的量。
1718 年,约翰第一,伯努利(J.Bernoulli I) 定义函数为由变量与常量以任何适当方式构成的量。
1755 年,欧拉在《微分学) 中给出更一般的定义,即函数都能用解析式表示,这也是当时数学家普遍的看法。
直到1807 年,傅里叶用三角级数表示更一般的函数后,函数才与其表达方式逐渐分离。
1837 年,狄利克雷用对应的观点给出了区间上的明确的函数定义,无须函数有解析表达式。狄利克雷的定义沿用至今,有重要的影响。
函数即映射的定义由戴德金(R.Dedekind) 于1887 年给出。
函数的概念极其广泛。例如,在公理化体系的概率定义中,概率实际上是一种定义在事件上满足三条公设的函数
2.2 映射的定义
两个非空集合A与B间存在着对应关系f,而且对于A中的每一个元素a,B中总有唯一的一个元素b与它对应,就这种对应为从A到B的映射,记作。
其中,b称为元素a在映射f下的像 ,记作:B。a称为b关于映射f的原像 。集合A中所有元素的像的集合称为映射f的值域,记作f(A)。
或者说,设A,B是两个非空的集合,如果按某一个确定的对应关系f,使对于集合A中的任意一个元素a,在集合B中都有唯一的元素b与之对应,那么就称对应B,B为从集合A到集合B的一个映射。
函数式中的映射, 配合下图理解更加。
映射,或者射影,在数学及相关的领域还用于定义函数。函数是从非空数集到非空数集的映射,而且只能是一对一映射或多对一映射。
映射在不同的领域有很多的名称,它们的本质是相同的。如函数,算子等等。这里要说明,函数是两个数集之间的映射,其他的映射并非函数。
一一映射(双射)是映射中特殊的一种,即两集合元素间的唯一对应,通俗来讲就是一个对一个(一对一)。
2.3 函数的定义
函数(function)即映射,设 X 与 Y 为给定的两个集合,f 是某个法则,每个按照 f 对应唯一的按照,称 f 为的一个函数(映射)。x 通过 法则 f 对应的 y 值记为,x 称为 自变量(independent variable),y 称为因变量。亦称“函数” 或“ y 是 x 的函数“。X 称为定义域;称为值域。
当Y = R时,函数称为实值函数 (real-valued function)。特别地,当 X,Y 均为实数集时,函数称为一元函数或一元实函数。
当 时,函数 是自变量,y 是因变量。
三、一元函数
一元函数是指函数方程式中只包含一个自变量。例如
四、二元函数
设D是二维空间的一个非空子集,称映射为定义在D上的二元函数,通常记为
或
其中点集D称为该函数的定义域,x、y称为自变量,z称为因变量.
上述定义中,与自变量x、y的一对值(即二元有序实数组)(x,y)相对应的因变量z的值,也称为f在点(x,y)处的函数值,记作,即.函数值的全体所构成的集合称为函数f的值域,记作,即
与一元函数的情形相仿,记号
或
”来表示D上的二元函数f.表示二元函数的记号f也是可以任意选取的.例如也可以记为等
函数式编程中柯里化可以将一个二元函数,转变为一元函数,这个函数将返回另一个一元函数。
curry: (X × Y → R) → (X → (Y → R))
五、单位元
5.1 概念
单位元(英文常写作Identity Element,即IE)是集合里的一种特别的元,与该集合里的运算(可理解为实数里的*,但并不局限于)有关。
当它和其他元素结合时,并不会改变那些元素。也叫幺元。
若称为右单位元;若称为左单位元,若称为单位元。
若该演算左右的元素能互换,左、右单位元相同,可称为双边单位元。
5.2 定义
单位元是集合里的一种特别的元素,与该集合里的二元运算有关。当单位元和其他元素结合时,并不会改变那些元素。单位元被使用在群和其他相关概念之中。
设为一带有一二元运算* 的集合S(称之为原群),则S内的一元素e被称为左单位元若对所有在S内的a而言,;且被称为右单位元若对所有在S内的a而言,。
而若e同时为左单位元及右单位元,则称之为双边单位元,又简称为单位元。
对应于加法的单位元称之为加法单位元(通常被标为0),而对应于乘法的单位元则称之为乘法单位元(通常被标为1)。这一区分大多被用在有两个二元运算的集合上,比如环。
下方identity模块会一起结合函数式编程讲一下单位元。
六、结合律
6.1 概念
在数学中,结合律(associative laws)是二元运算可以有的一个性质,意指在一个包含有二个以上的可结合运算子的表示式,只要算子的位置没有改变,其运算的顺序就不会对运算出来的值有影响.
6.2 定义
形式上,一个在集合S上的二元运算 被称之为可结合的若其满足下面的结合律。
运算的顺序并不会影响到表示式的值,且可证明这在含有“任意”多个 运算的表示式之下也依然是成立的。因此,当*是可结合的时,运算的顺序可以不需要去规范而不会使其意义不清,所以可以省略掉括号而简单写成:
不过,需要记住的是,改变运算的顺序并不包含或允许以移动表示式中的算子来改变其真实的运算。
6.3 编程结合律
加法结合律
var add = function(x, y) { return x + y };
add(add(x, y), z) == add(x, add(y, z));
乘法结合律
var multiply = function(x, y) { return x * y };
multiply(multiply(x, y), z) == multiply(x, mult(y, z));
类似的还有很多。
七、交换律
7.1 定义
乘法交换律是乘法运算的一种运算定律。两个因数相乘,交换因数的位置,积不变,叫做乘法交换律。多数相乘,任意两个数交换位置,其积不变。 用字母表示是:
三个数相乘时,可任意交换两个因数的位置,积不变,如:
它可以改变乘法运算当中的运算顺序,但不改变运算的最终结果。在日常计算中,乘法交换律往往可以简化问题的计算。
7.2 编程交换律
乘法交换律
var multiply = function(x, y) { return x * y };
multiply(x, y) == multiply(y, x);
八、分配律
8.1 定义
分配律是离散信号卷积和运算最常用的几个基本运算规则之一,离散序列卷和运算满足分配律,即两个序列先行相加运算再与第3个序列做卷和运算,其结果等于这两个序列分别与第3个序列先做卷和运算,然后二者再相加。即。
8.2 编程分配律
var add = function(x, y) { return x + y };
var multiply = function(x, y) { return x * y };
multiply(x, add(y, z)) == add(multiply(x, y), multiply(x, z));
九、无向图
边没有方向的图称为无向图
无向图G=<V,E>,其中:
- V是非空集合,称为顶点集。
- E是V中元素构成的无序二元组的集合,称为边集。
十、有向图
10.1 概念
一个有向图D是指一个有序三元组为关联函数,它使A(D)中的每一个元素(称为有向边或弧)对应于V(D)中的一个有序元素(称为顶点或点)对
10.2 可达性
对于一个无向图来说,如果它是连通的,那么它的任意两个顶点之问必存在一条路径,因此,通过这一路径可从一个顶点“到达”另一个顶点,若从顶点“可以到达u,则从u也可以到达“,也即v和u之间是互相可以到达的。
对于有向图,情形就不同了,因为存在从u到v的路径,并不蕴涵也存在从v到u的路径。
设D是一个有向图,且u、v∈D,若存在从顶点u到顶点v的一条路径,则称从顶点v到顶点u可达。
可达的慨念与从u到v的各种路径的数目及路径的长度无关。另外,为了完备起见,规定任一顶点到达它自身的是可达的。
可达性是一个有向图顶点的二元关系,依照定义,它是自反的,且是传递的。一般来说,可达不是对称的,也不是反对称的。
十一、偏序
11.1 概念
偏序集合(英语:Partiallyordered set,简写poset)是数学中,特别是序理论中,指配备了部分排序关系的集合。
这个理论将排序、顺序或排列这个集合的元素的直觉概念抽象化。这种排序不必然需要是全部的,就是说不必要保证此集合内的所有对象的相互可比较性。部分排序集合定义了部分排拓扑。
11.2 定义
设R是集合A上的一个二元关系,若R满足:
- Ⅰ 自反性:对任意,有;
- Ⅱ 反对称性(即反对称关系):对任意;
- Ⅲ 传递性:对任意。
则称R为A上的偏序关系,通常记作≼。注意这里的≼不必是指一般意义上的“小于或等于”。
若然有,我们也说x排在y前面(x precedes y)。
假设我们有这样的集合 R 是 A 上的小于等于关系也就是
所以
我们画出他的关系图:
显然他是自反的(环)、反对称的(无双向边)、传递的
不知道大家看到这里的大家有没有理解到偏序关系的本质:
偏序关系定义了一种方向,只能正着,反证就不行!
比如这里的小于等于就是只有一种方向,可以就不能
十二、全序
12.1 概念
在数学中,集合 X 上的全序关系(Total order),简称全序、又名线性序(linear order)、简单序(simple order),或(非严格)排序((non-strict) ordering),是在 X 上的反对称的、传递的和完全的任何二元关系。
12.2 定义
设集合X上有一全序关系,如果我们把这种关系用 ≤ 表述,则下列陈述对于 X 中的所有 a, b 和 c 成立:
- 如果 且 则 (反对称性)
- 如果 且 则 (传递性)
- 或 (完全性)
关系的完全性可以如下这样描述:对于集合中的任何一对元素,在这个关系下都是相互可比较的。
注意完全性条件蕴涵了自反性,也就是说,。因此全序也是偏序(自反的、反对称的和传递的二元关系)。全序也可以定义为“全部”的偏序,就是满足“完全性”条件的偏序。
可作为选择的,可以定义全序集合为特殊种类的格,它对于集合中的所有 a, b 有如下性质:
我们规定 当且仅当。可以证明全序集合是分配格。
全序集合形成了偏序集合的范畴的全子范畴,通过是关于这些次序的映射的态射,比如,映射 f 使得"如果 则 "。 在两个全序集合间的关于两个次序的双射是在这个范畴内的同构。
十三、范畴学
在哲学中,范畴(希腊语:κατηγορια)是指把事物进行归类所依据的共同性质。比如说时间{时刻/时段},空间{距离/体积},质地{质量/密度},关系{绝对/相对}等都是范畴。在分类学中,范畴指种类的本质。它不是种类本身,而是用来对事物进行分类的性质依据。
范畴是一种抽象程度最高的命题结构性概念,是哲学及其逻辑系统中最重要、最核心的概念。有大量的具体范畴经过无数次的感知验证和理知推演,已经成为人类抽象思维成果中具有高度概括性、结构稳定的概念。例如:
- 本性内涵{本体/本变}
- 本性外延{原料/价值}
- 原料{质地∨空间∨时间}
- 质地{质量/密度}
- 空间{距离/体积}
- 时间{时刻/时段}
- 价值{量值数/值}
等等,其中本性{本体/本变}是终极抽象范畴,是直接从无数的感知事实中抽象出来的,无须也不能够作演绎证明。其下各层次的所有概念都可以由它直接地或间接地推导出来。
13.1 范畴论
范畴论是抽象地处理数学结构以及结构之间联系的一门数学理论,以抽象的方法来处理数学概念,将这些概念形式化成一组组的“物件”及“态射”。有些人开玩笑地称之为“一般化的抽象废话”。范畴论出现在很多数学分支中,以及理论计算机科学和数学物理的一些领域。
研究范畴就是试图以“公理化”的方法抓住在各种相关连的“数学结构”中的共同特性,并以结构间的“结构保持函数”将这些结构相关起来。因此,对范畴论系统化的研究将允许任何一个此类数学结构的普遍结论由范畴的公理中证出。
考虑下面的例子:由群组成的类Grp包含了所有具有“群结构”的物件。要证明有关群的定理,即可由此套公理进行逻辑的推导。例如,由公理中可立即证明出,群的单位元素是唯一的。
不是只专注在有特定结构的个别物件(如群)上,范畴论会着重在这些物件的态射(结构保持映射)上;经由研究这些态射,可以学到更多关于这些物件的结构。以群为例,其态射为群同态。两个群间的群同态会严格地“保持群的结构”,这是个以将一个群中有关结构的讯息运到另一个群的方法,使这个群可以看做是另一个群的“过程”。因此,对群同态的研究提供了一个得以研究群的普遍特性及群公理的推论的工具。
十四、态射
14.1 概念
数学上,态射(morphism)是两个数学结构之间保持结构的一种过程抽象。
最常见的这种过程的例子是在某种意义上保持结构的函数或映射。例如,在集合论中,态射就是函数;在群论中,它们是群同态;而在拓扑学中,它们是连续函数;在泛代数(universal algebra)的范围,态射通常就是同态。
14.2 定义
一个范畴C由两个类给定:一个对象的类和一个态射的类。
有两个操作定义在每个态射上,域(domain,或源)和陪域(codomain,或目标)。
态射经常用从域到他们的陪域的箭头来表示,例如若一个态射f域为X而陪域为Y,它记为。所有从X到Y的态射的集合记为或者。(有些作者采用或)。
对于任意三个对象X,Y,Z,存在一个二元运算称为复合。的复合记为 或gf(有些作者采用fg)。态射的复合经常采用交换图来表示。例如
态射必须满足两条公理:
- 存在恒等态射:对于每个对象X,存在一个态射称为X上的恒等态射,使得对于每个态射我们有 。
- 满足结合律: 在任何操作有定义的时候。 当C是一个具体范畴的时候,复合只是通常的函数复合,恒等态射只是恒等函数,而结合律是自动满足的。(函数复合是结合的。)
注意域和陪域本身是决定态射的信息的一部分。例如,在集合的范畴,其中态射是函数,两个函数可以作为有序对的集合相等,但却有不同的陪域。这些函数从范畴论的目的来说被视为不同。因此,很多作者要求态射类是不交的。实际上,这不是一个问题,因为如果他们不是不交的,域和陪域可以加到态射上,(例如,作为一个有序三元组的第二和第三个分量),使得它们不交(互斥,disjoint)。
对态射和它们定义于其间的结构(或对象)的抽象研究构成了范畴论的一部分。在范畴论中,态射不必是函数,而通常被视为两个对象(不必是集合 )间的箭头。不象映射一个集合的元素到另外一个集合,它们只是表示域(domain)和陪域(codomain)间的某种关系。
尽管态射的本质是抽象的,多数人关于它们的直观(事实上包括大部分术语)来自于具体范畴的例子,在那里对象就是有附加结构的集合而态射就是保持这种结构的函数。
十五、identity
15.1 funtion
在数学的概念中,恒等函数为一无任何作用的函数
它总是传回和其引数相同的值。是不是很耳熟,结合上面单位元的例子。单位元的概念
若为单位元。
换句话说,在数学中,恒等函数为函数,输入等于输出。
15.2 functor
配上个函数式编程中的函子(functor)例子,如下例子为恒等函子(identity functor)
在写类似pointfree或者trace的时候,它往往可以帮到你。
var id = function(x) { return x };
// identity
compose(id, f) == compose(f, id) == f; // true
15.3 morphism
同样在范畴学中,的概念为恒等态射(identity morphism),没错就是上面讲到的态射的一种状态。
对于每个对象x,存在一个恒等态射(identity morphism)
其性质为,对于任何态射,有
恒等态射的含义是:定义了相同关系(equality relation A = B)。 可以简单的认为是;
15.4 What's the difference
这里可能会有疑问,函数式编程中的恒等函子与范畴学中的恒等态射有什么不同吗,看起来都是?
我认为比较通俗的说法是:
恒等态射是从某个对象到它自身的态射,而恒等函子可以返回传入它的对象和态射。
比较官方解答:
If 𝑋 is an object in a category C, the identity morphism is a morphism in the category C.
On the other hand, the identity functor is not a morphism in ; it is a functor A functor takes has two inputs, (i) objects, and (ii) morphisms. So the identity functor has, for every object in and morphism , the values and
It will be easier to see what's going on if you look at a specific category. So let's say is the category of Sets. Then an object in is just a set , and a morphism is just a set function.
In this case, the identity morphism of a fixed set is just the identity map on that you're surely familiar with. It just has the value for all
On the other hand, a functor assigns to every set a new set and to every morphism a new morphism . In the case of the identity functor, it just takes and .
十六、集合论
曾经,埃里特·比修普驳斥集合论是“上帝的数学,应该留给上帝"
集合论是从一个物件o和集合A之间的二元关系开始:若o是A的元素,可表示为。由于集合也是一个物件,因此上述关系也可以用在集合和集合的关系。另外一种二个集合之间的关系,称为包含关系。若集合A中的所有元素都是集合B中的元素,则称集合A为B的子集,符号为。例如{1,2} 是{1,2,3} 的子集,但{1,4} 就不是{1,2,3} 的子集。依照定义,任一个集合也是本身的子集,不考虑本身的子集称为真子集。集合A为集合B的真子集当且仅当集合A为集合B的子集,且集合B不是集合A的子集。
数的算术中有许多一元及二元运算,集合论也有许多针对集合的一元及二元运算:
- 集合A和B的并集,符号为,是在至少在集合A或B中出现的元素,集合{1,2,3} 和集合{2, 3, 4} 的联集为集合{1, 2, 3, 4} 。
- 集合A和B的**交集****,符号为,是同时在集合A及B中出现的元素,集合{1,2,3} 和集合{2, 3, 4} 的交集为集合{2, 3} 。
- 集合U和A的相对差集,符号为,是在集合U中,但不在集合A中的所有元素,相对差集{1,2,3} \ {2,3,4} 为{1} ,而相对差集{2,3,4} \ {1,2,3} 为{4} 。当集合A是集合U的子集时,相对差集也称为集合A在集合U中的补集。若是研究文氏图,集合U为全集时,且可以借由上下文找到全集定义时,会使用A来代替。
- 集合A和B的对称差,符号为或,是指只在集合A及B中的其中一个出现,没有在其交集中出现的元素。例如集合{1,2,3} 和{2,3,4} 的对称差为{1,4} ,也是其并集和交集的相对差集,或是二个相对差集的联集。
- 集合A和B的笛卡儿积,符号为A × B,是一个由所有可能的有序对(a,b)形成的集合,其中第一个物件是A的成员,第二个物件是B的成员。{1, 2}和{red, white}的笛卡儿积为{(1, red), (1, white), (2, red), (2, white)}。 集合A的幂集是指是以A的全部子集为元素的集合,例如集合{1, 2} 的幂集为{ {}, {1}, {2}, {1,2} } 。
一些重要的基本集合包括空集(唯一没有元素的集合),整数集合及实数集合。
十七、coproduct
17.1 概念
两个对象a和b是对象c的两个“注射剂”,因此对于任何其他具有两个注射剂的对象c'来说,存在一个从c到c'的唯一态射m,它将这些注入分解。其中m则称为coproduct
我们举个例子来理解coproduct,先看图
写成Hindley-Milner风格。
i :: a -> c
j :: b -> c
这时候,如果有一个c'也是通过a 和 b注入而来的话,c 与 c' 之间会有态射m。
根据态射规则可得
i' = m . i
j' = m . j
符合上述情形,一个与任何其他模式连接的唯一态射称为coproduct。
17.2 program
那么我们的程序里哪里用到了coproduct呢?
举个Typescript中的联合类型例子:
根据上述概念中的图片来思考代码,代码中我们同时向Vehicle注入了两个interface,这时Veicle是一种叠加态即联合类型,它极可能是number类型也可能是enum类型,并且c可能是不一样的,所以有了c 或者 c'...,所以在c, c'之间的唯一态射即coproduct。
函数式编程中的Either概念也是coproduct
十八、END
如果你可以认真逐行的看到这里的话,回过头在去看函数式编程或者一些什么别的语言中的一些设计思想,想必会有另一翻滋味。
我把上述都看完了之后不禁感慨
技术的尽头是哲学
往期文章推荐