函数式编程模式:单位元(幺元 Identity Element)

  单位元(又称幺元,英文Identity Element)是代数和函数式编程中常见的模式,它实际就是关于空的定义,是不是有点像老子道德经中的“无”“空虚”。

有一些确定的值 -可能散落在你现在正在工作的代码中 - 像数字1和0的一些值,空字符串""或空数组[],,这些空值经常用来实现初始化,是某种“空”的意思或没有任何意思。

单位元模式允许我们能以精确的方式将这些符号正式化。

想象一个空杯子,如何定义杯子是空的呢?日常生活或非正式场合很容易做到,但是从抽象角度思考,如何使用一个正式属性表达空杯呢?相对于不是空的杯子。

关键这里是定义一个附加操作来结合两种杯子,我们称为这个操作为“倒”,取出两个杯子,倒出里面的东西进入一个新的完全相同的容器中。

空杯

现在完成下面的操作如图,我们发现一个属性被完成:倒出空杯的内容进入另外一个杯子,留下另外一个杯子不变。

同样,将任何一个杯子倒出内容到这个空杯。

空杯

正式情况下,我们将这两个操作写如下代码,这里x代码任意杯子:

pour(emptyGlass, x) = x  
pour(x, emptyGlass) = x

我们使用其他空值来验证看看,比如数字0,我们假设这个操作是add,就是将两个数字加在一起,比如add(3,9)=12,那么代码如下:

add(0, x) = x  
add(x, 0) = x

这个0实际代表空,这样这个0是一个单位元,相对于我们的操作add。

同样在一个钟表时间内,用0-12小时表示时间,任何一个空时间也就是时间不增加也不减少,钟表时针也就停在原地不动,所以,这里空时间也是一个幺元或单位元。

再看看空数组,假设操作的合并数组concat,concat("foo", "bar") = "foobar"或concat([1,2,3], [4,5]) = [1,2,3,4,5],那么代码如下:

concat("", x) = x  
concat(x, "") = x

并且:

concat([], x) = x  
concat(x, []) = x

空数组和空字符串相对于合并操作来说是单位元,这样这些空能被正式描述。

通常情况下,一个集合A带有一个封闭操作 ⊕,一个空(identity)元素e是A中一个元素,这样,对于A中所有的x,我们有:

x ⊕ e = x  
e ⊕ x = x

我们可以到处寻找到单位元的例子,他们是很常见的,大量的隐藏在各个地方。让我们看更多的案例。

首先看看数字领域,有一个特殊值:无限大,,无限大看上去是空的反面,但是它也是一个单位元的案例,如何精确描述无限大是数字的空元素呢?

为了理解这个问题,可以想象一下上界,假定有一句语句:房间里最老的人是80左右的老人,这相当于定义了房间内人们年龄的信息,一种表达式是:房间内最老的人是几乎是年龄了。

无限大 作为上界,没有传递任何信息,因此在这个意义上是空的,我们选择什么符号表示这个概念呢?答案是使用min操作:

min(∞, x) = x;  
min(x, ∞) = x;

如果你愿意,可以试试负无穷大,选择合适操作证明它是空的。

现在看看多个空对象也许可以存在,配以不同操作以给予对空虚的某种理解。另外一个值是1带有操作mul,比如mul(5,3)=15

mul(1, x) = x;  
mul(x, 1) = x;

这种对空性的解释是自然的。

另外一个重要案例是单元函数(identity function),如(X=>X),或经常被称为id,单元函数是是一种将输入没有改变输出的函数,比如id(3)=3或id(x)=x

我们可以选择什么样的操作能看到这是一个单位元呢?这种操作通常是两个元素的自然组合方式,这样选择函数组合就是答案了:(f, g) => x => f(g(x)),或者经常被写为类似句号字符,由于无法找出这个字符集,这里显示的是正方形,实际应该是圆形。

f ∘ id = f  
id ∘ f = f

通过这个组合方式,我们看到单元函数其实也是一个单位元。

如果你想知道单位函数的名称从哪里来的,现在可以明白他是相对函数组合的单位元。

 

函数式编程模式:半群

函数编程专题

范畴category:组合的本质

加法是自然之道