我有一个应用程序,想要计算通过布尔运算(内部节点)组合的基元树(叶节点)的不同表示(网格,体素化,有符号距离函数,…).
我的第一个方法是为每个不同的表示编写一个带有虚拟getter函数的抽象基类,并在各个节点上缓存中间结果,只要它们的子树没有变化(这将刷新它们的缓存).
但是,我对树结构与每种不同表示的丑陋耦合感到不满意.为了缓解这种情况,我删除了抽象基类,而是为每个表示设置了一个访问者.
这将树与表示整齐地分离,但让我遇到了我现在需要在其他地方缓存中间结果的问题,这就是我的问题开始的地方.
TL; DR
如何在树的内部节点缓存(任意多种不同类型的)中间值,而不使树依赖于值类型?
我的方法
要求提供两种选择:
>将数据存储在树中但具有类型擦除
>将数据存储在树外,并以某种方式将其“连接”到节点
第一个让我对一些效率问题感到困惑:我可以轻松地在节点中添加一个boost :: any(或类似的东西)的容器,但是每个访问者都必须在整个容器中搜索它自己的数据.
第二个中的分离引入了使高速缓存保持最新到当前树的问题.如果树中有更改(删除,更改节点),则缓存的值必须至少无效.我的直觉是使用一些哈希函数和一个unordered_map但我也遇到了一些问题:
>我不能使用treenodes本身作为键,所以我需要引入另一个只引用树节点并在树中表示它们的类
>引用unordered_map键中的值需要擦除所有删除了引用的条目,或者我们在unordered_map中有一个悬空引用(/指针),它可能会在rehash中被触发
>树中的更改需要重建unordered_map,因为键可能已更改
我错过了一些明显的解决方案吗?
你喜欢哪种方法(以及为什么)?
最佳答案 我曾遇到类似的问题,我的解决方案如下:
>让每个节点都有唯一的标识符.
>让每个节点都有一个版本号.使节点的计算值无效的修改只会增加版本号.
>让每个访问者都有一个缓存映射,其中ID对是密钥,映射到版本/值对.
>当(重新)走树时,在地图中查找节点的条目.如果版本正确,请使用缓存的值.如果它已过时,请计算新值并替换旧版本/值对.
起初,我使用节点的地址作为ID,但出于内存原因,我不得不重复使用子树并选择节点的路径作为ID.这样的路径具有以下优点:可以由每个访问者计算并且不需要存储在节点处.在我的例子中,每个节点最多可以有两个子节点,因此路径只是一组左/右决策,可以存储在一个简单的无符号整数中,并带有一些位移(我的树永远不会达到32的深度) ,所以一个32位无符号绰绰有余的关键).