我最近购买了 Joseph Felsenstein 的《推断系统发育》,这是一本关于推断系统发育树的数学和计算方法的好书,并且一直在尝试实现它所描述的一些算法。
具体来说,我有兴趣在具有持久性数据结构的功能设置中这样做,因为许多方法都涉及遍历可能的树空间,并且通过结构来廉价地记住我们去过的地方的历史会很好共享(这是 aphyr 在这篇博文中对“世界”所做的事情)、轻松缓存先前计算的子树值等。
这样做的问题是,很多方法都涉及“重新生根”树,我无法弄清楚如何以纯粹的功能方式廉价地做到这一点。基本上我需要一些方法来捕捉以下每一个的想法(使用 clojure 表示法,将树表示为向量):
[:a [:b [:c :d]]]
[:b [:a [:c :d]]]
[:a [:b [:d :c]]]
[:b [:a [:d :c]]]
[[:a :b] [:c :d]]
[[:c :d] [:a :b]]
[:c [:d [:a :b]]]
[:d [:c [:a :b]]]
[:c [:d [:b :a]]]
[:d [:c [:b :a]]]
表示相同的数据,仅在根的位置不同;它们各自代表无根树:
a b
\ /
|
/ \
c d
我希望能够使用 zipper 导航到其中一棵树,然后调用一个函数reroot
,该函数将返回一个新树,该树以这样一种方式压缩,即根位于 current loc
。
在书中,Felsenstein 描述了一种用于廉价可重根树的数据结构,它看起来像下面匆忙制作的图表
其中圆圈是结构,箭头是指针。结构环是树上的内部节点,一旦我们引用了一个,我们可以通过一些指针交换将根移到那里。不幸的是,这是一个变异操作,需要相互引用,这两者在纯函数设置中都是不可能的。
我觉得应该有一种方法可以使用拉链做我想做的事,但我已经玩clojure.core/zip
了一段时间了,却一无所获。
有谁知道这样的实现,或者对我应该阅读的东西/我应该看的论文/如何做到这一点的想法有建议?
谢谢!