基本上,我知道如何创建图形数据结构并在允许副作用的编程语言中使用 Dijkstra 算法。通常,图算法使用一种结构将某些节点标记为“已访问”,但这会产生副作用,这是我试图避免的。
我可以想到一种用函数式语言实现这一点的方法,但它基本上需要将大量状态传递给不同的函数,我想知道是否有更节省空间的解决方案。
Basically, I know how to create graph data structures and use Dijkstra's algorithm in programming languages where side effects are allowed. Typically, graph algorithms use a structure to mark certain nodes as 'visited', but this has side effects, which I'm trying to avoid.
I can think of one way to implement this in a functional language, but it basically requires passing around large amounts of state to different functions, and I'm wondering if there is a more space-efficient solution.
发布评论
评论(6)
您可以查看 Martin Erwig 的 Haskell 函数图形库 是如何工作的。例如,其 最短路径函数都是纯粹的,你可以看到源代码 了解其实现方式。
另一种选择,就像提到的 fmark 一样,是使用一个抽象,它允许您根据状态实现纯函数。他提到了 State monad(在 惰性 和 严格品种)。另一种选择,如果您使用 GHC Haskell 编译器/解释器(或者我认为任何支持 2 级类型的 Haskell 实现),另一种选择是 ST monad,它允许您编写处理可变变量的纯函数内部。
You might check out how Martin Erwig's Haskell functional graph library does things. For instance, its shortest-path functions are all pure, and you can see the source code for how it's implemented.
Another option, like fmark mentioned, is to use an abstraction which allows you to implement pure functions in terms of state. He mentions the State monad (which is available in both lazy and strict varieties). Another option, if you're working in the GHC Haskell compiler/interpreter (or, I think, any Haskell implementation which supports rank-2 types), another option is the ST monad, which allows you to write pure functions which deal with mutable variables internally.
我只是将访问的集合保留为一个集合并将其作为参数传递。任何有序类型的集合和超高效整数集合都有有效的日志时间实现。
为了表示图,我使用邻接列表,或者使用有限映射将每个节点映射到其后继列表。这取决于我想做什么。
我推荐 Chris Okasaki 的 纯函数式数据结构,而不是 Abelson 和 Sussman 。我已经链接到克里斯的论文,但如果你有钱,他将其扩展为 很棒的书。
只是为了一笑,这里有一个有点可怕的反向后序深度优先搜索,在 Haskell 中以连续传递的方式完成。这直接来自 Hoopl 优化器库:
I just keep the visited set as a set and pass it as a parameter. There are efficient log-time implementations of sets of any ordered type and extra-efficient sets of integers.
To represent a graph I use adjacency lists, or I'll use a finite map that maps each node to a list of its successors. It depends what I want to do.
Rather than Abelson and Sussman, I recommend Chris Okasaki's Purely Functional Data Structures. I've linked to Chris's dissertation, but if you have the money, he expanded it into an excellent book.
Just for grins, here's a slightly scary reverse postorder depth-first search done in continuation-passing style in Haskell. This is straight out of the Hoopl optimizer library:
如果您使用的是我熟悉的唯一函数式语言 Haskell,我建议使用 状态单子。 State monad 是一个函数的抽象,它接受一个状态并返回一个中间值和一些新的状态值。这是被认为是惯用的haskell对于那些需要维持大国的情况。
它是初学者函数式编程教程中强调的天真的“将状态作为函数结果返回并将其作为参数传递”习惯用法的更好替代方案。我想大多数函数式编程语言都有类似的构造。
If you were using haskell, the only functional language with which I am familiar, I would recommend using the State monad. The State monad is an abstraction for a function that takes a state and returns an intermediate value and some new state value. This is considered idiomatic haskell for those situations where maintaining a large state is necessary.
It is a much nicer alternative to the naive "return state as a function result and pass it as a parameter" idiom that is emphasized in beginner functional programming tutorials. I imagine most functional programming languages have a similar construct.
这是一个 Swift 示例。您可能会发现这更具可读性。与超级神秘的 Haskell 示例不同,这些变量实际上是描述性命名的。
https://github.com/gistya/Functional-Swift-Graph
Here is a Swift example. You might find this a bit more readable. The variables are actually descriptively named, unlike the super cryptic Haskell examples.
https://github.com/gistya/Functional-Swift-Graph
我很想听听一些非常聪明的技术,但我认为有两种基本方法:
这就是函数式编程中所做的事情。如果编译器/解释器有任何好处,它将帮助您管理内存。特别是,如果您碰巧在任何函数中进行递归,您将需要确保使用尾递归。
I would love to hear about some really clever technique, but I think there are two fundamental approaches:
That is what's done in functional programming. If the compiler/interpreter is any good, it will help manage memory for you. In particular, you'll want to make sure that you use tail recursion, if you happen to recurse in any of your functions.
大多数函数式语言都支持内部函数。因此,您可以在最外层创建图形表示,然后从内部函数引用它。
本书广泛介绍了它 http://www.amazon.com/gp/product/0262510871 /ref=pd_lpo_k2_dp_sr_1?ie=UTF8&cloe_id=aa7c71b1-f0f7-4fca-8003-525e801b8d46&attrMsgId=LPWidget-A1&pf_rd_p=486539851&pf_rd_s=lpo-top-stripe-1& ;pf_rd_t=201&pf_rd_i=0262011530&pf_rd_m =ATVPDKIKX0DER&pf_rd_r=114DJE8K5BG75B86E1QS
Most functional languages support inner functions. So you can just create your graph representation in the outermost layer and just reference it from the inner function.
This book covers it extensively http://www.amazon.com/gp/product/0262510871/ref=pd_lpo_k2_dp_sr_1?ie=UTF8&cloe_id=aa7c71b1-f0f7-4fca-8003-525e801b8d46&attrMsgId=LPWidget-A1&pf_rd_p=486539851&pf_rd_s=lpo-top-stripe-1&pf_rd_t=201&pf_rd_i=0262011530&pf_rd_m=ATVPDKIKX0DER&pf_rd_r=114DJE8K5BG75B86E1QS