当前位置: 首页 > news >正文

译自由幺半群

上一篇:极限与余极限

原文地址:https://bartoszmilewski.com/2...

不论是在范畴论中还是在编程中,幺半群都是个重要的观念。范畴对应着强类型的语言,而幺半群对应着无类型的语言。因为在幺半群里你可以复合任意两个箭头,就像无类型语言中你可以复合任意两个函数一样(当然,在你执行程序时可能以一个超时的错误而告终)。

我们已经看过幺半群可以描述成一个单对象的范畴,在那个范畴里所有的逻辑都被变成了态射的复合规则。这个范畴模型和更传统的集合论意义下的幺半群定义完全等价,那个定义是说我们把两个元素“相乘”得到第三个元素。这个“乘法”过程可以被进一步解剖为先构建一个元素的序对然后把这个序对指定为一个已有的元素——它们的“积”。

我们把乘法的第二部分——把序对指定为已有的元素——丢掉会发生什么?比如说,我们可以从任意一个集合出发,构建所有可能的序对,然后把它们叫做新的元素。然后我们用这些新的元素再次构建所有可能的序对,以此类推。这是一个链式反应——我们会不不休止地加入新的元素。这样的结果是个无限集,几乎就是一个幺半群。但一个幺半群还需要一个单位元和结合律。当然没问题,我们可以添加一个特殊的单位元并且令某些序对相等——刚刚好满足单位元和结合律。

让我们看看在具体例子里这是怎么做的。我们从一个有两个元素的集合{a, b}出发。我们将会把它们叫做自由幺半群的生成元。首先,我们添加一个特殊元素e作为单位元。接着,我们加入元素的所有序对叫做“积”。ab的积就是序对(a, b)ba的积就是序对(b, a)aa的积就是序对(a, a)bb的积就是序对(b, b)。我们还能用e构建像(a, e)(e, b)等这样的序对,但我们会把它们指定成ab,等等。所以这一轮我们只添加了(a, a)(a, b)(b, a)(b, b),并得到了集合{e, a, b, (a, a), (a, b), (b, a), (b, b)}

bunnies

下一轮里我们会加进来一些像(a, (a, b))((a, b), a)等等的元素。这时我们就必须要保证满足结合律,所以我们会把(a, (b, a))指定为((a, b), a)等等。换句话说,我们不需要内部括号。

你可以猜一下这个过程的最后结果是什么:我们会创造出ab的所有可能的列表。实际上,如果我们用空列表表示e,我们就会看到我们的“乘法”和列表连接没有任何不同。

这种不断生成可能的元素组合并且做一个最小数目的指定——仅仅为了满足法则——的构造就叫一个自由构造。我们刚刚做的就只是用生成元{a, b}构造了一个自由幺半群

Haskell中的自由幺半群

Haskell中的两个元素的集合等价于Bool类型,这个集合生成的自由幺半群等价于[Bool]类型(Bool的列表)。(我故意忽略了无限大列表的问题)

Haskell的幺半群是用类型类定义的:

class Monoid m where
    mempty  :: m
    mappend :: m -> m -> m

这只是在说每个Monoid必须有一个叫mempty的幺元和一个叫mappend的二元函数(乘法)。单位元(律)和结合律不能在Haskell中表达,必须在每次实例化幺半群的时候有程序员验证。

任意类型a的列表构成一个幺半群的事实由这个实例的定义描述:

instance Monoid [a] where
    mempty  = []
    mappend = (++)

这是说空列表[]是单位元,而列表连接运算(++)就是那个二元运算。

就像我们已经看到的那样,一个a类型的列表对应着由集合a充当生成元的自由幺半群。自然数集带上乘法运算并不是一个自由幺半群,因为我们指定了太多的积。作为例子比较一下:

2 * 3 = 6
[2] ++ [3] = [2, 3] // 和[6]不同
此处作者举的例子多少有些令人混淆。在原文的评论区也有很多人和作者讨论了该问题,想看相关内容的读者可以移步原文评论区。自然数的确不可以一个自由幺半群,就算选择素数集作为生成元, 2*3=3*2=6这样的交换律也是在自由幺半群定义之外的东西。此处作者的例子大概是想说在自然数中 2*3=6,而在自由幺半群中不可以把这样一个乘法算式的结果明确指定为像 6这样的某个既有的数字(即这种指定一定不是自由幺半群所必需的,从而导出的必然不是自由幺半群),只能表示为序对 [2, 3]的形式。译者注。

这很容易懂,但问题是,我们能否在范畴论中表示这种自由构造,而在范畴论中我们无法观察对象的内部?我们将会用到我们的主角:泛构造。

第二个感兴趣的问题是,是否任意一个幺半群都能通过一些,比那些仅仅满足法则的最少数目的指定更多的指定,来从自由幺半群获得?你会看到泛构造可以直接得到它。

自由幺半群的泛构造

如果你想想我们之前有关泛构造的经验,你可能就会注意到它并不是那么像挑一个最符合某个模式的对象那样构造对象的。所以如果我们想用泛构造“构造”一个自由幺半群的话,我们必须考虑幺半群的一整个族然后从里面挑一个。我们需要一整个要选取(对象)的幺半群的范畴。但幺半群能构成范畴吗?

让我们先把幺半群看作带上附加结构的集合,附加结构由单位元和乘法定义。我们要选一些保持幺半群结构的函数作为态射。这种能保持结构的函数被称作同态。一个幺半群的同态必须把两个元素的积映为这两个元素像的积:

h (a * b) = h a * h b

而且它必须把单位元映为单位元。

这个同态的定义就是抽象代数中的定义,注意,等号前后的两个乘法所在的幺半群是不一样的。译者注。

比如,考虑一个从整数列表到整数的同态。如果我们把[2]映为2,把[3]映为3,那我们就必须把[2, 3]映为6,因为连接操作

[2] ++ [3] = [2, 3]

变成了乘法

2 * 3 = 6

现在让我们忘掉那些个幺半群的内部结构,仅仅把它们看作这些对应的态射连接的对象。你就能得到一个幺半群范畴Mon

好吧,可能刚刚我们忘掉了内部结构,让我们来看一个重要的性质。每个Mon的对象都能平凡地映为一个集合。这就仅仅是它地元素组成的集合。这个集合叫做承载集。事实上,我们不仅能把Mon的对象映为集合,我们还能把Mon的态射(同态)映为函数。还是那句话,现在看起来有些无趣,但马上就能用到了。这个从MonSet的态射实际上是个函子。因为这个函子“忘掉”了幺半群的结构——一旦我们到了扁平的集合上,我们就不会再区分谁是单位元或者关心乘法了——它叫做遗忘函子。遗忘函子经常出现在范畴论中。

我们现在有了两种不同的看待Mon的视角。我们可以像其他的包含对象和态射的范畴那样看它。这种视角下,我们不会看到幺半群的内部结构。我们对某个特定的对象所能说的就只有它通过态射和它自己以及其他对象相关联。态射的“乘法”表——复合规则——是来源于另一个视角:作为集合的幺半群。就算步入了范畴论我们也不能完全丢掉这种视角——我们仍然要通过遗忘函子使用它。

为了用泛构造,我们需要定义一个特殊的性质,这个性质可以让我们对幺半群范畴排序然后挑出最好的那个当作自由幺半群。但是自由幺半群是通过它的生成元定义的。不同的生成元的选择会导出不同的自由幺半群(一个Bool列表和一个Int列表并不一样)。我们的构造必须从一个生成元的集合开始,所以我们回到了集合!

这就是遗忘函子能够显身手的地方了。我们可以用它给我们的幺半群拍X光片。我们可以在这些家伙的X片上指定生成元。它是这么工作的:

我们从一个生成元集合x出发。这是个Set中的集合。

我们想要匹配的模式由一个幺半群m——Mon的一个对象——和一个Set中的函数p构成:

p :: x -> U m

其中U就是我们的从MonSet遗忘函子。这是个奇怪的异质模式——一半在Mon里一半在Set里。

想法是函数p会在m的X片中指定生成元的集合。在集合中指定一些点的函数(它们可能会坍缩这些点)有些讨厌,不过没关系。它们都会在泛构造中排序,而这会挑出那个最好的代表。

monoid-pattern

这里可能有些令人费解,但只要注意到x是已经预先给定的的话,这一切就会顺理成章:也就是说,函数 p已知一个预先给定的生成元集合 x,然后把 x的每一个元素都映为 m的集合中的某个元素,这就像是从 m的集合中挑一些元素作为其生成元集,并且这个新集合不能比 x更大。

我们还必须定义候选者们的排名方式。假定我们还有另一个候选者:一个幺半群n和在它的X片中指定生成元的一个函数:

q:: x -> U n

我们说mn更好是说存在一个幺半群的态射(保持结构的同态):

h :: m -> n

它在U下的像(记住,U是个函子,所以它会把态射映为函数)通过p因式化了(其他这样的函数):

q = U h . p

如果你把p看作选取m的生成元;q看作在n中选择“同样的”生成元;然后你就能把h看作两个幺半群的生成元间的映射了。记住,由定义,h可以保持幺半群的结构。这就意味着一个幺半群的两个生成元的积会映射到第二个幺半群的两个对应的生成元的积,以此类推。

monoid-ranking

这种排名方式就能用来找到最好的那个候选者——自由幺半群了。这儿是定义:

我们说 m(连同函数 p)是生成元为 x的自由幺半群当且仅当有 唯一的m到其他幺半群 n(连同函数 q)的满足上述因式分解性质的态射 h
(容我再自作多情地提醒一下,所有用泛构造方式所做的定义中的唯一均是指在同构的意义下唯一,这里也不例外,译者注。)

恰好,这就可以回答我们的第二个问题。函数U h就可以把U m的多个对象坍缩成U n的一个对象。这个坍缩对应了在自由幺半群中对更多的元素进行指定的过程。因此,任意的由生成元x得到的幺半群都可以通过对x上自由幺半群进行一些指定来得到。而自由幺半群就是仅进行最小数目的指定而得到的那个。

我们在讲到伴随时还会遇到自由幺半群。

挑战

  1. 你可能会觉得(就像我最初那样)幺半群同态要保持单位元的条件是多余的。毕竟,我们知道 对于所有的a

    h a * h e = h (a * e) = h a

    所以h e就是一个右结合的单位元(类似地,左结合的单位元)。问题在于对所有的ah a可能只能取到靶幺半群的一个子群。这样在h的像之外就可能有个“真正”的单位元。 请证明两个幺半群之间保持乘法的态射必须自动地保持单位元。

  2. 考虑一个从带有列表连接操作的整数列表到带有乘法的整数的幺半群同态。空列表[]的像是什么?假定所有的单元素列表被映射为他们中的那个整数,就是说[3]映为3,等等。[1, 2, 3, 4]的像是什么?有多少个不同的列表映为整数12?这两个幺半群之间是否还有别的同态?
  3. 单例集生成的自由幺半群是什么?你能不能把它看成什么东西的同构?

致谢

感谢Gershom Bazerman检查我的数学和逻辑,以及André van Meulebrouck在整个系列中的编辑上的帮助。

下一篇:可表函子

相关文章:

  • rabbitmq远程消费者生产者发送端接收端实例
  • Vuex and Typescript
  • shell日志搜索命令
  • 毕玄:我在阿里的十年技术感悟
  • NLPIR智能语义技术让大数据挖掘更简单
  • 开源Pravega架构解析:如何通过分层解决流存储的三大挑战?
  • Android AutoCompleteTextView和MultiAutocompleteTextView实现动态自动匹配输入的内容
  • socketserver
  • Python Revisited Day 04 (控制结构与函数)
  • 开源引路人:我的Apache Mentor之路
  • 一次性能优化:吞吐量从1提升到2500
  • Eureka自我保护机制与Eureka服务发现(Discovery)
  • __proto__ 和 prototype的关系
  • 这道js题你会吗?
  • java B2B2C源码电子商城系统-Spring Cloud Eureka自我保护机制
  • 分享一款快速APP功能测试工具
  • #Java异常处理
  • 【跃迁之路】【585天】程序员高效学习方法论探索系列(实验阶段342-2018.09.13)...
  • Android开源项目规范总结
  • codis proxy处理流程
  • create-react-app项目添加less配置
  • Java IO学习笔记一
  • javascript从右向左截取指定位数字符的3种方法
  • JAVA并发编程--1.基础概念
  • js算法-归并排序(merge_sort)
  • oschina
  • PHP CLI应用的调试原理
  • Python - 闭包Closure
  • React Transition Group -- Transition 组件
  • React 快速上手 - 07 前端路由 react-router
  • SpiderData 2019年2月16日 DApp数据排行榜
  • Vue 重置组件到初始状态
  • 翻译:Hystrix - How To Use
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 使用API自动生成工具优化前端工作流
  • 微信小程序设置上一页数据
  • 再谈express与koa的对比
  • ​香农与信息论三大定律
  • #android不同版本废弃api,新api。
  • #laravel 通过手动安装依赖PHPExcel#
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • (TipsTricks)用客户端模板精简JavaScript代码
  • (第61天)多租户架构(CDB/PDB)
  • (分布式缓存)Redis持久化
  • (附源码)springboot建达集团公司平台 毕业设计 141538
  • (转)Linux下编译安装log4cxx
  • (转)如何上传第三方jar包至Maven私服让maven项目可以使用第三方jar包
  • (转)总结使用Unity 3D优化游戏运行性能的经验
  • ..thread“main“ com.fasterxml.jackson.databind.JsonMappingException: Jackson version is too old 2.3.1
  • .NET Core引入性能分析引导优化
  • .NET 反射的使用
  • .net/c# memcached 获取所有缓存键(keys)
  • .netcore 6.0/7.0项目迁移至.netcore 8.0 注意事项
  • .net实现客户区延伸至至非客户区
  • .Net组件程序设计之线程、并发管理(一)