Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Recursion 如何在Clojure中将有序树转换为命名节点的集合?_Recursion_Clojure_Tree - Fatal编程技术网

Recursion 如何在Clojure中将有序树转换为命名节点的集合?

Recursion 如何在Clojure中将有序树转换为命名节点的集合?,recursion,clojure,tree,Recursion,Clojure,Tree,我认为最好举个例子。假设我有一个有序的树: (def abcd [:a [:b :c] :d]) 我想从中构建一个键值映射的集合,每个映射表示这棵树的一个节点,带有一个随机名称和所有相关信息,即它的父节点(对于根节点为nil)、它的索引(0、1、2..),如果它是叶节点,则它的内容(如“:a”)。例如,在这种情况下,可能是: [{:name G_u36654,:parent nil,:index 0} {:名称G_36655,:内容:a,:父G_36654,:索引0} {:名称G_u36656

我认为最好举个例子。假设我有一个有序的树:

(def abcd [:a [:b :c] :d])
我想从中构建一个键值映射的集合,每个映射表示这棵树的一个节点,带有一个随机名称和所有相关信息,即它的父节点(对于根节点为nil)、它的索引(0、1、2..),如果它是叶节点,则它的内容(如“:a”)。例如,在这种情况下,可能是:

[{:name G_u36654,:parent nil,:index 0}
{:名称G_36655,:内容:a,:父G_36654,:索引0}
{:名称G_u36656,:父G_u36654,:索引1}
{:名称G_36657,:内容:b,:父G_36656,:索引0}
{:名称G_36658,:内容:c,:父G_36656,:索引1}
{:名称G_u36659,:内容:d,:父G_u36654,:索引2}]
我定义了一个函数,它似乎做了我想做的事情,但它通过调用自身使用递归,我很难弄清楚如何使用循环递归,我相信一定有更好的方法。以下是我的尝试:

(defn mttrav "my tree traversal"
  ([ptree parent index]
   (let [name (gensym)]
     (cond
       (not (coll? ptree)) [ {:name name :content ptree :parent parent :index index}]

       :else (reduce into
                     [{:name name  :parent parent :index index}]
                     (map-indexed #(mttrav %2 name  %1) ptree)))))
  ([ptree]
   (mttrav ptree nil  0)))
顺便说一句,我不知道向量是否是正确的集合,也许一个集合更有意义,但我使用向量是为了更容易调试,因为当节点生成的顺序被保留时,它更可读,如果节点意外重复,我想看到它

提前谢谢


编辑:为了澄清,每个节点也可以有一个:子节点的列表,而不是:父节点,以及其他一些变体,只要它是一个平面地图集合,每个地图代表一个节点,具有唯一的:名称,并且节点的位置、内容和父子关系在该结构中被捕获。预期的输入是通常来自Instaparse的hiccup解析树,映射将成为要插入Clara会话的记录。

当树拒绝尾部递归时,另一种尝试是Clojure标准库中的“拉链”。拉链非常适合编辑,但它们也非常擅长线性化深度优先遍历,同时保持结构上下文可用。典型的拉链环如下所示:

user> (def abcd '(:a (:b :c) :d))
#'user/abcd'
user> (loop [ret [], z (zip/seq-zip abcd)] 
        (if (zip/end? z)
          ret
          (let [o {:name 42, :content (zip/node z), :parent 42, :index 42}]
            (recur (conj ret o) (zip/next z)))))
[{:name 42, :content (:a (:b :c) :d), :parent 42, :index 42}
 {:name 42, :content :a, :parent 42, :index 42}
 {:name 42, :content (:b :c), :parent 42, :index 42}
 {:name 42, :content :b, :parent 42, :index 42}
 {:name 42, :content :c, :parent 42, :index 42}
 {:name 42, :content :d, :parent 42, :index 42}]
要填写
:parent
:index
,您可以在官方文档中找到拉链符号,用于查找父母的“向上”,查找兄弟姐妹的“向左”,等等


我使用
seq-zip
创建了zip,将节点建模为一个列表。您的特定案例将节点建模为向量,而
seq-zip
无法识别,因此您可能会使用
vector-zip
或发明自己的适配器。您可以按照文档中的“源”链接查看
seq-zip
vector-zip
的工作原理

当一棵树拒绝尾部递归时,另一个尝试是Clojure标准库中的“拉链”。拉链非常适合编辑,但它们也非常擅长线性化深度优先遍历,同时保持结构上下文可用。典型的拉链环如下所示:

user> (def abcd '(:a (:b :c) :d))
#'user/abcd'
user> (loop [ret [], z (zip/seq-zip abcd)] 
        (if (zip/end? z)
          ret
          (let [o {:name 42, :content (zip/node z), :parent 42, :index 42}]
            (recur (conj ret o) (zip/next z)))))
[{:name 42, :content (:a (:b :c) :d), :parent 42, :index 42}
 {:name 42, :content :a, :parent 42, :index 42}
 {:name 42, :content (:b :c), :parent 42, :index 42}
 {:name 42, :content :b, :parent 42, :index 42}
 {:name 42, :content :c, :parent 42, :index 42}
 {:name 42, :content :d, :parent 42, :index 42}]
要填写
:parent
:index
,您可以在官方文档中找到拉链符号,用于查找父母的“向上”,查找兄弟姐妹的“向左”,等等


我使用
seq-zip
创建了zip,将节点建模为一个列表。您的特定案例将节点建模为向量,而
seq-zip
无法识别,因此您可能会使用
vector-zip
或发明自己的适配器。您可以按照文档中的“源”链接查看
seq-zip
vector-zip
的工作原理

宽度优先遍历是您所需要的。因此,如果要在遍历树时构建父节点列表,首先需要唯一标识所有叶节点。我不确定不这样做是否可以完成,除非您确定您的leafs节点是唯一的。这里也变得很晚/很早了,所以我的大脑不能正常工作。我相信我的解决方案可以被浓缩很多

因此,如果您有一个类似于[:a[:b:c]:d[:b:c]]的树,[:b:c]是:b和:c的父节点,但最后两个叶节点也是:b和:c,那么您选择哪一个父节点

让我们来看一棵树,它的叶子有唯一的id

(定义附加ID[树]
(clojure.walk/postwark(fn[node]
(if(coll?node)节点
{node:id(gensym)})
树)
(def树(附加ID[:a[:b:c]:d]))
;; 产生这个
;; [{:node:a,:id G__21500}
[{:节点:b,:id G_uu21501}{:节点:c,:id G_u21502}]
{:node:d,:id G_u21503}]
现在来了解解决方案的其余部分


(defn add parent[父映射id分支]
(关联父映射id{:子映射id(集合(映射:id分支))
:子节点(映射:节点分支)})
(defn查找父id[节点父映射]
(->)父映射
(过滤器(fn[[parent id{children id:children id}]]
(包含子id(:id节点)))
(第一次)
(defn find index[节点父映射树]
(如果让[父id(查找父id节点父映射)]
(让[子节点(:子节点(获取父映射父id))]
(.indexOf子节点(:node节点)))
(.indexOf树节点)))
(定义bfs[树]
(循环[队列树]
父映射{}
ret[]]
(如果(不是空队列)
(let[节点(第一个队列)
rst(vec(休息队列))]
(续)
(地图?节点)
(再次出现
父映射
(连接ret(关联节点:父节点(查找父id节点父映射)
:索引(查找索引节点父映射树)))
(向量?节点)
(让[父id(gensym)]
(重复(进入rst节点)
(添加父映射父id节点)
(conj ret{:id父id)
:索引(查找索引节点父映射树)
:parent(查找父id节点父映射)}()()()))
(ret)

(def树(附加ID[:a[:b:c]:d]))