我希望在Newick format中打印一棵二叉树,显示每个节点与其父节点的距离。目前我没有遇到下面的代码使用正则递归的问题,但是太深的树可能会产生堆栈溢出。以纽尼克格式懒洋洋地打印一棵树
(defn tree->newick
[tree]
(let [{:keys [id children to-parent]} tree
dist (double to-parent)] ; to-parent may be a rational
(if children
(str "(" (tree->newick (first children))
"," (tree->newick (second children))
"):" dist)
(str (name id) ":" dist))))
(def example {:id nil :to-parent 0.0
:children [{:id nil :to-parent 0.5
:children [{:id "A" :to-parent 0.3 :children nil}
{:id "B" :to-parent 0.2 :children nil}]}
{:id "C" :to-parent 0.8 :children nil}]})
(tree->newick example)
;=> "((A:0.3,B:0.2):0.5,C:0.8):0.0"
(def linear-tree (->> {:id "bottom" :to-parent 0.1 :children nil}
(iterate #(hash-map :id nil :to-parent 0.1
:children [% {:id "side" :to-parent 0.1 :children nil}]))
(take 10000)
last))
(tree->newick linear-tree)
;=> StackOverflowError
我已经与当前的工具,如tree-seq
和clojure.walk
发现的问题,是我要拜访的内部节点不止一次,以夹着逗号和关闭支架。我用clojure.zip
,但没有设法写一个懒/尾递归实现,因为我需要为每个内部节点存储它们已经访问了多少次。
虽然我不能够维护它,但令人印象深刻!要在面向对象的范例中编写更好的代码,你必须学习模式;要在功能范例中更好地编写代码,您必须学习计算机科学。 –
@BrunoKim:如果可以的话,看一看The Little Schemer一书。第8章(Lambda the Ultimate)的结尾处理了延续传球的风格。为了给出一个简短的解释,它基本上用包装其他闭包的闭包代替了调用堆栈。而蹦床只是一个巧妙的小诀窍,可以在“recur”支持的特殊情况之外进行尾递归:只要返回值是一个函数,蹦床就会调用它。 –