【问题标题】:Continuation Passing Style (CPS) during graph construction图构建期间的继续传递样式(CPS)
【发布时间】:2011-11-17 17:39:13
【问题描述】:

我正在开发一个用于细分曲面的库。为了表示网格拓扑,我使用了一种分裂顶点板条数据结构(参见左侧的图表)。

在构建网格的过程中,也可以将其视为图形,它创建的节点应该指向另一个尚不存在的节点(参见右侧的图表 - 虚线箭头代表未来的链接)。经典的解决方案是创建一个带有空指针的节点,然后在创建另一个节点时更新它。由于我正在研究 Haskell :) 并且我不想进入代码的阴暗面(杂质),我想知道是否可以在不更新数据的情况下构建网格(图形)。我猜 CPS(Continuation Passing Style)可以完成这项工作,但我想不出办法。

这只是一场梦吗?

更新

让我稍微澄清一下我的问题。我正在寻找一种方法来创建具有直接链接(指针)的节点,直接链接是指没有中间表或映射。只是这样的普通数据定义:

data Mesh = Edge Vertex Mesh Mesh Vertex | Ground

如果我没记错并且可行的话,CPS 将允许高效地创建(无需节点更新)和高效的横向(无需在地图上查找)图。另一方面,图将变得完全不可变,即需要将单个更改传播到整个图,例如更改列表的尾部。

我错了吗?如果没有,怎么办?

【问题讨论】:

  • 您应该注意您的陈述的局限性。 Haskell 的原则将禁止您确定Mesh 的内涵身份;也就是说,你想打结,但你无法确定,如果你去某个地方又回来,你实际上是回来了,而不是在无限循环结构中的其他地方。在数学中,图是在任意顶点的“载体集”上定义的;我建议对纯函数图也这样做。

标签: haskell graph continuations continuation-passing


【解决方案1】:

您需要一种称为tying the knot 的技术。它利用惰性评估来完成工作。不需要 CPS。

假设您可以通过某个唯一 ID(字符串、整数或其他)来识别每个节点。还假设当您创建一个节点时,您已经知道它指向的所有节点的 ID,无论它们是否已经创建。然后你就可以使用这个技巧了。

您可以通过图形创建函数将nodes :: Data.Map NodeID Node 字符串化(使用状态单子以更加方便)。创建节点时,将其添加到地图中。当您创建应该指向名为x 的节点的边时,您使用fromMaybe $ lookup nodes x是否已经创建了名为 x 的节点,或者将来会创建节点,这并不重要。只要它在某个时候创建​​的,你就设置好了。它只会在您需要时从地图中获取。

这就是我用来根据文字描述创建图表的方式。也许还有其他更好的方法。

如果在创建节点时,您不知道节点将指向的所有节点的 ID,则需要稍微修改此技术,例如将地图从节点 ID 传递到其邻居列表,并逐步构建每个列表。

在完成构建图之前,您应该小心并避免评估惰性值。

【讨论】:

  • 这不起作用,因为当您插入注释时,会创建一个新地图,因此您可能指的是该节点永远不会存在的地图版本。相反,使用fromList 或类似方法一次构建地图。这样,您可以轻松地参考最终地图来打结。编辑:实际上,您仍然可以这样做,只需确保查找中使用的地图是 final 地图,而不是当前地图。
  • @hammar:是的,说得好。我忘记了我实际上遇到了这个问题。我最终在查找中使用了最终地图。
  • “习惯于”创建图表?你现在是怎么做到的?
  • 在均匀网格的情况下,这将是一个很好的解决方案,其中标签预测很简单。正如您所说,对于一般情况,应该做一些工作来解决问题。我的问题更多关于 CPS 的可能使用(请参阅我的问题的更新)。如果 CPS 不起作用,那么我会接受你的建议。谢谢
【解决方案2】:

它没有使用 CPS...但我正在为 Haskell 开发一个平面图形库,使用与您上面描述的类似的方案。通过指定在它之前或之后的现有边来添加边。

实际的图实现已经完成,剩下的就是让二进制序列化工作和高性能(使用 PLANAR_CODE 作为初学者,也可以使用 Graph6 和 Sparse6)以及其他一些额外的东西。

目前您使用单独的函数获得对偶图(这似乎也是您绘制的图),但我正在考虑在每次添加边时计算对偶图(假设是连通图)。

代码可从darcs get @987654321@获取; http://code.haskell.org/~ivanm/dangd/ 有一个示例用法(这是我开发这个库的目的)。


取自 Haddock 文档作为使用示例:

例如,让g 参考下图(其中 n1等既是标签又是变量名):

     ====                    ====
    ( n1 )                  ( n2 )
     ====                    ====





                             ====
                            ( n3 )
                             ====

我们可以在n1n2 之间添加一条边(使用Anywhere 作为 EdgePos,因为目前两个节点上都没有边):

 ((e1,e2),g') = addEdge n1 Anywhere n2 Anywhere "e1" "e2" g

这将产生以下图表:

                  e2
     ====  <---------------  ====
    ( n1 )                  ( n2 )
     ====  --------------->  ====
                  e1




                             ====
                            ( n3 )
                             ====

如果我们想在n2n3 之间添加边,我们有三个 n2上的位置选项:

  • 使用Anywhere:因为只有一条边,所以没有 在第二条边的嵌入方面存在差异。

  • 放置新边BeforeEdge e2(顺时针绕n2)。

  • 放置新边AfterEdge e2(顺时针绕n2)。

由于n2 目前只有一条边,所有三个 EdgePos 值 将产生相同的图表,因此我们可以任意选择一个:

 ((e3,e4),g'') = addEdge n2 (BeforeEdge e2) n3 Anywhere "e3" "e4" g'

但是,对于更多的边缘,必须注意哪个 EdgePos 值被使用。结果图是:

                  e2
     ====  <---------------  ====
    ( n1 )                  ( n2 )
     ====  --------------->  ====
                  e1         |  ^
                             |  |
                          e3 |  | e4
                             |  |
                             v  |
                             ====
                            ( n3 )
                             ====

相同的图表(直到实际的 Edge 值;所以它不会满足 ==) 将通过以下方式获得:

 ((e4,e3), g'') = addEdge n3 Anywhere n2 (BeforeEdge e2) "e4" "e3" g'

【讨论】:

  • 我一直在研究您的代码,您正在使用地图的插入和查找函数来构建图形。我使用 CPS 的想法是避免任何地图的查找或更新。请参阅我的问题中的更新。谢谢
  • @LambdaStaal:我只是将其作为替代方案 :) 虽然我能问一下为什么您如此反对在底层地图上查找或更新吗?
  • 请不要误会我的意思。我不反对他们!此外,我会使用它们。即使 CPS 在这里工作,将其保留在生产代码中似乎也是一个过于复杂的想法。我只是想知道是否可以在没有更新或基础表的情况下创建图表。我想它会更有效,更奇怪的是,我认为这是更自然的解决方案。感谢您的宝贵时间!
  • @LambdaStaal:好的,我认为您有一些特殊的原因想要在没有它们的情况下在这里实现图表,而不仅仅是想知道是否可以完成。我想在我的用例中我需要能够更新/更改图表,我认为这是一般情况(而不是静态图表)。
【解决方案3】:

看来您不需要将指向 NextA 和 NextB 边缘的链接存储在 Edge 内。由于这些是可以通过从当前 Edge 遍历来计算的,为什么不编写一个函数来获取 Edge 并返回其 NextA / NextB 边缘,这与基于 Edge 的 A 和 B 部分的顺时针方向的图表一样。

【讨论】:

  • 对不起,我没听明白。您是说要计算 NextA/B,然后将其存储在表/地图上并用于将来的搜索。你能澄清你的答案吗?对我来说 NextA 或 NextB 是它的横向过程的一部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-17
  • 2012-07-04
  • 2015-02-10
  • 1970-01-01
  • 2010-12-21
  • 2011-02-16
  • 2016-12-20
相关资源
最近更新 更多