如何在函数式编程语言中实现图形和图形算法?

时间:2010-06-08 16:03:50

标签: functional-programming computer-science graph

基本上,我知道如何创建图形数据结构,并在允许副作用的编程语言中使用Dijkstra算法。通常,图算法使用一种结构将某些节点标记为“已访问”,但这有副作用,我试图避免这种情况。

我可以想到一种在函数式语言中实现它的方法,但它基本上需要将大量的状态传递给不同的函数,我想知道是否有更节省空间的解决方案。

6 个答案:

答案 0 :(得分:17)

你可以查看Martin Erwig的Haskell functional graph library如何做事。例如,它的shortest-path functions都是纯粹的,你可以看到它source code的实现方式。

另一个选项like fmark mentioned是使用抽象,它允许你根据状态实现纯函数。他提到了州monad(有lazystrict种可供选择)。另一个选择,如果你在GHC Haskell编译器/解释器(或者,我认为,任何支持rank-2类型的Haskell实现)中工作,另一个选项是the ST monad,它允许你编写纯函数来处理内部有可变变量。

答案 1 :(得分:3)

如果您使用haskell,这是我熟悉的唯一功能语言,我建议您使用 State monad 。 State monad是一个函数的抽象,它接受一个状态并返回一个中间值和一些新的状态值。对于需要维持大状态的情况,这是considered idiomatic haskell

它是一个更好的替代品,将天真的“返回状态作为函数结果并将其作为参数传递”成语,这在初学者函数式编程教程中得到了强调。我想大多数函数式编程语言都有类似的构造。

答案 2 :(得分:2)

我只是将访问集保留为集合并将其作为参数传递。有任何有序类型和超高效整数集的集合的高效日志时间实现。

为了表示图形,我使用邻接列表,或者我将使用将每个节点映射到其后继列表的有限映射。这取决于我想做什么。

而不是Abelson和Sussman,我推荐Chris Okasaki的Purely Functional Data Structures。我已经和克里斯的论文联系在一起,但如果你有钱,他就把它扩展为excellent book


只是为了笑容,这是一个稍微可怕的反向后序深度优先搜索在Haskell中以延续传递方式完成。这直接来自Hoopl优化器库:

postorder_dfs_from_except :: forall block e . (NonLocal block, LabelsPtr e)
                          => LabelMap (block C C) -> e -> LabelSet -> [block C C]
postorder_dfs_from_except blocks b visited =
 vchildren (get_children b) (\acc _visited -> acc) [] visited
 where
   vnode :: block C C -> ([block C C] -> LabelSet -> a) 
                      -> ([block C C] -> LabelSet -> a)
   vnode block cont acc visited =
        if setMember id visited then
            cont acc visited
        else
            let cont' acc visited = cont (block:acc) visited in
            vchildren (get_children block) cont' acc (setInsert id     visited)
      where id = entryLabel block
   vchildren bs cont acc visited = next bs acc visited
      where next children acc visited =
                case children of []     -> cont acc visited
                                 (b:bs) -> vnode b (next bs) acc     visited
   get_children block = foldr add_id [] $ targetLabels bloc
   add_id id rst = case lookupFact id blocks of
                      Just b -> b : rst
                      Nothing -> rst

答案 3 :(得分:0)

我很想听听一些非常聪明的技巧,但我认为有两种基本方法:

  1. 修改一些全局状态对象。即副作用
  2. 将图形作为参数传递给函数,返回值为修改后的图形。我认为这是“传递大量国家”的方法
  3. 这就是在函数式编程中所做的。如果编译器/解释器有任何好处,它将帮助您管理内存。特别是,如果您碰巧在任何函数中进行递归,您将需要确保使用尾递归。

答案 4 :(得分:0)

这是一个Swift示例。您可能会发现这更具可读性。这些变量实际上是以描述性命名的,与超级神秘的Haskell示例不同。

https://github.com/gistya/Functional-Swift-Graph

答案 5 :(得分:-1)