Alga教程

介绍

在这里,您将学习以下基本知识阿尔加图代数的实现。每个给定的示例都是可运行的,所以请随时安装alga(带有阴谋集团堆栈)如果你想尝试这个代码,在你附近有一个GHCi控制台。你只需要在代数。图表模块。别犹豫,看看模块文档如果你想了解更多信息。

如果您遇到任何错误(我希望您不会),请在https://github.com/snowleapard/alga/issues/.

图形定义

这个问题

传统上,图被定义为由一个集合组成的一对V(V)顶点和集合E类 ⊆ V(V)×V(V)边缘。在使用传统命令式语言时,这很好,但在尝试将其用于Haskell等函数式语言时会导致一些问题。

alga的思想是使用图形的另一种定义,更“功能友好”。由于“功能友好的”数据结构的大部分是递归的,因此alga的图形定义如下:

解决方案

数据 图表= 清空
             | 顶点
             | 覆盖(图表a)(图表a)
             | 连接(图表a)(图表a)

所以它说:

  1. 只有使用构造函数才能构造空图清空这不需要任何论证。

  2. 你可以从任何东西构造一个图,使用构造函数在单个顶点中变换它顶点.

  3. 你可以叠加两个图形,也就是说把它们一个接一个地放在一起。

    形象

  4. 您可以连接两个图形,即从左侧的每个顶点到右侧的每个顶点绘制一条边。

    形象

很简单,不是吗…好吧,这不是一种标准的查看图表的方法,但不用担心,你会习惯的。

请记住:创建边的唯一方法是使用 连接.

这个定义允许我们处理定向的图:从顶点1到顶点2的边与从顶点2到顶点1的边不同。

一些示例

那么,如何使用这个定义呢?以下是一些示例:

我在桌面上听到你说:

“伯克,但是手写大图形会变得非常烦人!”

别担心,有一些捷径。

深入定义

Num实例

覆盖连接看起来像操作符,我们想将其用作。因此我们摆出:

(+)= 覆盖
(*)= 连接

事实上,如果我们有号码例如,我们可以使用顶点。这导致了这种情况:

实例 号码=> 号码(图表a)哪里
    from整数 = 顶点 . from整数
(+)= 覆盖
(*)= 连接
    符号      = 常数 清空
    防抱死制动系统         = 身份证件
    否定      = 身份证件

这意味着,在图表,我们有顶点1==1,这很有用!

你明白为什么alga是代数图表数量?这里有很多数学!不,请不要像在墓地里看到僵尸一样逃跑!别担心,这不是很难的数学。

注释

我们将使用(+)(*)符号,但即使在处理任何图形时,这些定律也是正确的。

覆盖

像往常一样,(+)相联的(选择覆盖图形的顺序并不重要):

(1 + 2)+  == 1 +(2 + )

(+)也是可交换的(覆盖b条与叠加相同b条):

1 + 2 == 2 + 1

(+)清空作为中性元素(覆盖清空图到另一个图是此图):

1 + 清空 == 1 == 清空 + 1

(+)幂等的(将图形与自身重叠是同一个图形):

1 + 1 == 1

连接

像往常一样,(*)相联的(选择连接图形的顺序并不重要):

(1 * 2)*  == 1 *(2 * )

(*)不是可交换的(从顶点1到顶点2绘制边与从顶点2到顶点1绘制边不同):

1 * 2 /= 2 * 1

(*)它有清空作为中性元件(连接清空图到另一个图是此图):

1 * 清空 == 1 == 清空 * 1

(*)可以饱和(连接同一个图形的三倍与连接同一图形的两倍相同)

1 * 1 * 1 == 1 * 1

为什么?(*)不是幂等的? 因为将顶点与其自身连接可以创建一个:

形象

他们俩在一起

当你发现你可以混合时,你会提醒吗+*在同一方程式中?这里也是一样的!

1 *(2 + )== 1 * 2 + 1 * 

将单个顶点1连接到2和3可以通过两个步骤完成相等的方式:

哎呀,这件事办好了,我们可以向前迈进一步。

制作图表

我还没有回答这个问题:

定义可用吗?我们可以用alga表示法来表示每个图吗?

让我们试着回答这个问题重要的问题。如前所述,图(几乎总是)被定义为一对V(V)顶点和E类 ⊆ V(V)×V(V)一组边。为了证明我们可以表示任何图形,我们需要定义一个函数创建::[a]->[(a,a)]->图形从这个标准表示创建一个图形。

让我们忘记边缘:我们首先要做的是顶点::[a]->图形a将顶点列表转换为图表包含所有单个顶点。看起来我们要折叠一份清单

顶点::【a】-> 图表
顶点= 文件夹(\v克-> 覆盖(顶点v) g)清空

知道怎么做吗边::[(a,a)]->图形a? 同样的方式,很明显:

边::[(a,a)]-> 图表
边缘= 文件夹
(\(x,y)g-> 覆盖(连接(顶点x)(顶点y) )克)
  清空

那么,我们的创建::[a]->[(a,a)]->图形? 简单地说:

创建::【a】->[(a,a)]-> 图表
创建v e= 覆盖(顶点v)(边e)

所以我们已经定义了所需的函数,因此我们可以安全地使用alga的定义!

定义的好处

折叠

这种表示法的一个优点是能够定义折叠功能,一种适应折叠对于图形:

文件夹::b条->(a)->b)->(b)->b条->b)
      ->(b)->b条->b)-> 图表->b条
折叠视频=
    哪里
清空         =e(电子)
去(顶点x)=v x(v x)
去(覆盖x年)=o(转到x)(转到y)
去(连接x y)=c(去x)(去y)

换句话说折叠函数获取基本情况清空图形,用于转换的对象顶点当我们遇到覆盖连接.

转置

我们有一个很棒的图表,我们想转置换位有向图包括反转所有边的方向。使用折叠,这是小菜一碟:

转置:: 图表-> 图表
转置=折叠清空 顶点 覆盖(轻弹 连接)

诱导

还不相信吗?让我们尝试构建一个归纳子图。诱导子图是“忘记”某些顶点以及这些顶点之间的所有边的子图。

所以我们将对induce::(a->bool)->图a->图a功能。我们将使用折叠当然。

基本情况是什么?我们需要更改清空图表?显然,一点也不:

诱导::(a)-> 布尔)-> 图表-> 图表
诱导谓词=折叠
  清空
  未定义
  未定义
  未定义

然后,如果我们遇到一个顶点,我们需要验证它是否满足谓词。如果没有,我们将简单地替换它清空图形!

诱导::(a)-> 布尔)-> 图表-> 图表
诱导谓词=折叠
  清空
(\x-> 如果谓词x然后 顶点x个其他的 清空)
  未定义
  未定义

最后,我们需要接触基本图之间的连接吗?一点也不!记得,清空是的中性元素二者都 连接覆盖因此,我们可以将空图放在结构中而不会出现问题(不用担心,真正的实现会去掉这些空图)。因此,我们得出以下结论:

诱导::(a)-> 布尔)-> 图表-> 图表
诱导谓词=折叠
  清空
(\x-> 如果谓词x然后 顶点x个其他的 清空)
  覆盖
  连接

这么简单,不是吗?

这甚至允许我们定义:

删除顶点::-> 图表-> 图表
移除顶点x=诱导(/=x)

hasEdge公司

折叠诱导非常酷,Alga API的很大一部分都是由它们制成的。例如,让我们看一下hasEdge公司定义:

hasEdge:: 订单=>->-> 图表-> 图表
hasEdge uv=
(连接(顶点u)(顶点v)`是SubgraphOf`).
诱导(`元素`[u,v])

检查图形是否包含来自x个,可以移除不同的每个顶点x个,然后检查边缘单独地是诱导子图的子图。请注意hasEdge公司需要一个订单实例,因为是SubgraphOf需要它。

定义的问题

平等

在藻类中没有规范的方法来定义图。例如:

覆盖(顶点 1) (顶点 2)
== 覆盖(顶点 2) (顶点 1)
== 连接 清空(覆盖(顶点 1) (顶点 2))
== 覆盖
(连接(顶点 1)清空)
(连接 清空(顶点 2))

幸运的是,由于等式实例(提供(==))帮你解决这个问题。

Alga还提供(===)其中表示结构的平等,因此:

覆盖(顶点 1) (顶点 2)
===
覆盖(顶点 2) (顶点 1)
== False(错误)

定义函数时要小心

下面是一个你可以定义的讨厌的函数:

关闭: 图表 A类 -> 图表 A类
关闭清空         = 清空
关闭(顶点x)= 顶点x个
关闭(覆盖x年)= 连接x年
关闭(连接x年)= 连接x年

你看到问题了吗?

>>> x个= 顶点 0
>>> = 覆盖(顶点 0) (顶点 0)
>>>x个==
真的
>>> 打印(接近x)
顶点 0
>>> 打印(接近y)
(顶点 0)*(顶点 0)
>>>关闭x==接近y
False(错误)

目前,人们可能会弄乱内部结构,等式失去了其意义(即∀((f):G公司第页第页小时 A类G公司第页第页小时 B类) :  =  ⟹ (f)  = (f) 不保持)。

有用的实例

Alga的图是一些经典Haskell类的实例:

Eq,秀

当然,您有Graph等式,并且可以显示Graph。藻类也可以出口到DOT文件格式通过代数。图表。出口。模块。

订单

更不重要的是,有一个在图上定义并在alga中实现的总顺序。它使用size-lexicographic/comparison:

因此,首先,它确实是和顺序(这个关系是传递的、自反的和反对称的),它是全部的(你可以比较任何图表)。第二,这种顺序在某种程度上与图形操作兼容:

Functor(仿真器)

毫不奇怪,图表是的实例Functor(仿真器):

实例 Functor(仿真器)(图表a)哪里
  功能性维修计划_清空 = 清空
  功能性维修计划(f)(顶点a)= 顶点 $如果
  功能性维修计划(f)(覆盖a b)= 覆盖(功能性维修计划f a)(功能性维修计划f b)
  功能性维修计划(f)(连接a b)= 连接(功能性维修计划f a)(功能性维修计划f b)

这意味着如果您要转换在一个b条然后可以转换图a图b例如:

形象

如果您想测试它,alga表示中的第一个图形是:1*(2+5)*0

警觉的!警觉的!哈斯克勒的警报响了!如果有Functor(仿真器)例如,有一个莫纳德一个?

莫纳德

图表确实是一个莫纳德实例:

实例 莫纳德(图表a)哪里
  返回  = 顶点
>>=(f)=折叠清空(f)连接 覆盖

您可以将任何东西转换为图形,只需将其转换为单个顶点即可。此外,如果可以从类型生成图形然后可以替换图形a结果,将其转换为图b.

例如,可以重新定义先前查看的诱导作为:

诱导::(a)-> 布尔)-> 图表-> 图表
归纳谓词g
  =>>=(\x-> 如果谓词x然后 顶点x个其他的 清空)

可折叠和可穿越

图表可以是有效的可折叠的和有效的可穿越的实例,但这些是定义在代数。图表模块。
原因是这些实例与库的其余部分不兼容。例如,vertexList g/=到列表g因为顶点可以在结构中多次出现。

例如:社交网络

目标

好吧,现在我们想用这一切来构建一些真实的东西。比如说一个社交网络:人们可以很容易地通过图形来表示它们。营销团队分析了市场,决定做一些“a la Twitter”。顶点将是用户,边缘来自x个将表明x个正在跟踪.

handleRequest(处理请求)

员工会议选择您建立手柄请求功能:

类型 用户 = 国际

数据 请求M = 添加用户 用户
              | 删除用户 用户
              | 连接U 用户 用户
              | 断开U 用户 用户

handleRequestM:: 请求M -> 图表 用户 -> 图表 用户

现在这是一项非常简单的工作,而且实现非常简单:

handleRequestM:: 请求M -> 图表 用户 -> 图表 用户
处理请求M(添加用户a)= 覆盖(顶点a)
处理请求M(删除用户a)=removeVertex删除顶点a
处理请求M(连接Ua b)=
    覆盖(连接(顶点a)(顶点b) )
处理请求M(断开Ua b)=删除边缘a b

检查

鉴于你很快就实现了自己的功能,我们要求你帮助一位同事完成他的功能。他正在研究getFollowing::用户->图形用户->[用户]功能。

一种可能的方法是使用edgeList::Ord a=>图a->[(a,a)]功能。

获取以下信息: 用户 -> 图表 用户 ->[用户]
获取关注u=
  地图 信噪比 . 滤波器(\(v,_)->单位==v).边缘列表

您甚至可以盲目地实现获取关注者功能:

获取关注者:: 用户 -> 图表 用户 ->[用户]
获取关注者u=
  地图 fst公司 . 滤波器(\(_,v)->单位==v).边缘列表

正在进行IO

好的,纯的图表检查很酷,但如何使用IO进行检查?您的上级希望不时了解有多少用户连接。他写过已连接 ::用户->IO Bool,他让你写信已连接的数量 ::图形用户->IO Int.使用导线在顶点列表中,您可以快速回答:

已连接的数量: 图表 用户 -> IO(输入输出) 国际
已连接的数量= 功能性维修计划(长度 . 滤波器 身份证件).
折叠(纯净的 清空) (功能性维修计划 顶点 .已连接)(提升A2覆盖)(提升A2连接).
顶点列表

注意,这个版本很容易理解和编写,但效率不高。可以使用折叠IntSet(IntSet):

进口 有资格的 数据。IntSet(IntSet) 作为 设置
进口 控制。适用(提升A2)

已连接的数量: 图表 用户 -> IO(输入输出) 国际
已连接的数量= 功能性维修计划设置.大小.折叠
(返回设置为空)
(\x-> 功能性维修计划
(\年-> 如果
       然后设置单体x
       其他的设置为空
)
(已连接x)
)
(提升A2组活接头)
(提升A2组活接头)