我试图在Haskell中为图形创建数据类型,如下所示:
type Vertex a = a
type Edge a = (Vertex a, Vertex a)
data Graph a = Combine [Vertex a] [Edge a]
这是一个适用于我想做的表示,但我意识到可能存在不在顶点列表中的顶点的边 .
我的问题是,是否有可能确保每个边只包含顶点列表中的顶点?
我已经考虑了很多,但到目前为止我得到的最好的想法是一个函数,它将一个新的图形与边缘列表中所有缺少的顶点添加到顶点列表中 . 就像是:
fix_graph :: Graph a -> Graph a
fix_graph (Combine v e) = Combine (removeDuplicates (v ++ [x | (x,_) <- e] ++ [y | (_,y) <- e])) e
removeDuplicates :: [t] -> [t]
...
因为这个想法并没有真正满足我(也因为我没有花时间来很好地实现它),我想知道是否有可能有一个数据构造函数来添加不在顶点的边缘的顶点 - 立即列出 .
我已经阅读了答案here,但我还有其他任何可能解决这个问题的方法 .
如果有人可以帮我解决问题或摆脱我的幻想,那将会有所帮助......
提前致谢
1 回答
所以有几个不同的选择:
结结
有很多方法可以在Haskell中编码图形 . 最简单的方法是使用一个名为“绑结”的过程在树数据结构中创建圆度 . 例如,对此图进行编码:
您只需编写一个节点作为其名称和子项列表:
这有很多方便:您现在可以像任何其他Haskell数据结构一样遍历数据结构,具有尾递归函数 . 要在循环中终止,在递归时将当前项目添加到
path
变量上,并且递归逻辑应该说的第一件事是,| node
elempath = ...
来处理您想要的循环 .另一方面,你的次要一致性问题已经引发了一些棘手的一致性问题 . 考虑一下这两者之间的区别:
所以这很糟糕,我唯一真正的答案是,“通过转换为下面的一个来规范化......” .
无论如何,上面的技巧也通过名称相互递归和letrec,并且基本上意味着在
where
或let
子句中,你放在那里的所有定义都可以"see each other" . 它不是懒惰的 property ;您也可以使用严格的语言来创建上述数据结构 - 但是这种方式理解相互递归定义的功能严格语言的语言设计可能有点困难 . (使用非功能性语言,您只需根据需要创建指针 . )明确的数字命理学
现在想想你是如何得到的,并将其转换为你的代表 . 最简单的方法是通过一个包含
Array
的中间步骤:现在,这一致性问题要少得多,现在可以通过编程方式缓解这些问题 . 但是,如果要以编程方式使用State monad创建此类图形,并且希望暂时将数据结构保持在不一致状态,直到读取正确的节点,则这些一致性问题可以起作用 . 唯一的缺点是,当您将图形写入文件时,它看起来有点难以理解,因为数字不如字符串友好:
你可以用一个
Map String [String]
来解决这个问题,因为访问的权衡变成了O(log n)
. 在任何情况下,你都应该学习这种表示:你要转换为IntMap [Int]
并在你做出你提议的"completeness checks"时返回 .一旦你得到这些,事实证明你可以使用支持
Array Int Node
来创建递归[Node]
,如上所述:边缘列表
一旦你有了上面的列表(Map.toList或Array.assocs),边缘列表变得非常容易:
翻转方面稍微复杂一点,完成了你想要直接做的事情:
也就是说,我们使用 Map 来遍历边缘列表,该 Map 将键映射到他们的子集;我们将其初始化为映射到空集的顶点列表(以便我们可以在图中使用断开的单个节点),然后通过将值插入到键的集合中来遍历边缘,如果我们不这样做则创建该集合看到钥匙 .