我正在为细分曲面创建一个库。为了表示网格拓扑,我使用了一种分裂顶点板条数据结构(参见左侧的图表)。
在构建网格时,也可以看作图形,它创建的节点应指向另一个尚不存在的节点(参见右侧图表 - 虚线箭头表示未来链接)。经典的解决方案是创建一个带空指针的节点,然后在创建另一个节点时更新它。因为我正在研究Haskell :)而且我不想去代码的黑暗面(杂质)我想知道是否有可能在不更新数据的情况下构造网格(图形)。我想CPS(延续传递风格)可以完成这项任务,但我无法找到方法。
这只是一个梦吗?
更新
让我稍微澄清一下我的问题。我正在寻找一种方法来创建具有直接链接(指针)的节点,并通过直接链接我的意思是没有中间表或映射。只是一个简单的数据定义:
data Mesh = Edge Vertex Mesh Mesh Vertex | Ground
如果我没有错,并且如果它是可行的,CPS将允许有效创建(没有节点更新)和图的有效横向(没有在地图上查找)。另一方面,图形将变得完全不可变,即,需要在整个图形中传播单个变化,例如,改变列表的尾部。
我错了吗?如果不是,该怎么做?
答案 0 :(得分:4)
您需要的是一种称为tying the knot的技术。它利用懒惰的评估来完成工作。不需要CPS。
假设您可以通过某个唯一ID(字符串,整数或其他)来标识每个节点。还假设在创建节点时,您已经知道它指向的所有节点的ID,无论它们是否已经创建。然后你可以使用这种技术。
通过图表创建功能将nodes :: Data.Map NodeID Node
字符串(为了方便起见,使用状态monad)。创建节点时,将其添加到地图中。当您创建应指向名为x
的节点的边时,您使用fromMaybe $ lookup nodes x
。 名为x的节点是否已创建,或将来是否会创建并不重要。只要 在某个时刻创建,就会被设置。它只会在您需要时从地图中获取。
这是我用来从文字描述中创建图表的方式。也许还有其他更好的方法。
如果在创建节点时,您不知道节点将指向的所有节点的ID,则需要稍微修改此技术,例如:将地图从节点ID传递到其邻居列表,并逐步构建每个列表。
在构建图表之前,您应该小心并避免评估惰性值。
答案 1 :(得分:2)
它不使用CPS ......但我正在为Haskell开发一个平面图库,使用与上述相似的方案。通过指定现有边缘在其之前或之后来添加边缘。
实际的图形实现已经完成,剩下的就是让二进制序列化工作和执行(使用PLANAR_CODE作为初学者,也可能是Graph6和Sparse6)以及其他一些额外的东西。
目前你使用单独的函数获得双重图形(这似乎也是你所绘制的图形),尽管我正在考虑每次添加边时计算双重(假设连接图形)。
代码可以从darcs get http://code.haskell.org/~ivanm/planar-graph/
获得;示例用法(我正在为此开发此库的用途)位于http://code.haskell.org/~ivanm/dangd/
。
取自Haddock文档作为使用示例:
例如,让g
参考下图(其中
n1
等都是标签和变量名称:
==== ====
( n1 ) ( n2 )
==== ====
====
( n3 )
====
我们可以在n1
和n2
之间添加边缘(使用 Anywhere 作为
EdgePos ,因为两个节点上当前没有边缘):
((e1,e2),g') = addEdge n1 Anywhere n2 Anywhere "e1" "e2" g
这将产生以下图表:
e2
==== <--------------- ====
( n1 ) ( n2 )
==== ---------------> ====
e1
====
( n3 )
====
如果我们想在n2
和n3
之间添加边,我们有三个
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'
答案 2 :(得分:0)
似乎您不需要将链接存储到Edge内的NextA和NextB边缘。由于这些是可以通过从当前Edge遍历计算的东西,为什么不编写一个带有Edge并返回其NextA / NextB边缘的函数,该边缘与Edge的A和B部分的顺时针方向相同。