比方说,我有一个清单列表,代表Clojure中的树结构,例如
'(a (b (c d)) (e (f)))
,我想将其转换为这样的记录格式(以将其传递给可视化程序包):
[{:id "0" :label "a" :parent nil}
{:id "1" :label "b" :parent "0"}
{:id "2" :label "c" :parent "1"}
{:id "3" :label "d" :parent "1"}
{:id "4" :label "e" :parent "0"}
{:id "5" :label "f" :parent "4"}]
解决这个问题的正确方法是什么?
我对此感到很不满意,但我会考虑从defrecord
开始,然后再考虑通过树循环的一种方式,但我不知道该如何开始。
(def tree '(a (b (c d)) (e (f))))
(defn list-to-record [l]
(defrecord rec [id name parent])
(let [counter (atom 0)]
(into [] (map ->rec
... ... ...))))
(list-to-record tree)
也许我应该使用clojure.walk
?
编辑:澄清一下,无论标签是什么,它都应该起作用,因此更改输入列表中的标签对结果结构(每个:id的:parent值)不起作用。也就是说,下面的列表与上面的一样,但标签彼此相同
'(a (a (a a)) (a (a)))
应该翻译成
[{:id "0" :label "a" :parent nil}
{:id "1" :label "a" :parent "0"}
{:id "2" :label "a" :parent "1"}
{:id "3" :label "a" :parent "1"}
{:id "4" :label "a" :parent "0"}
{:id "5" :label "a" :parent "4"}]
答案 0 :(得分:1)
这是使用Clojure拉链和loop
+ recur
的一种方法:
(defn index-zipper [z]
(loop [loc z, next-id 0, parent-ids [], acc []]
(cond
(z/end? loc) acc
(and (z/node loc) (not (z/branch? loc)))
(recur
(z/next loc)
(inc next-id)
(cond
(some-> (z/right loc) z/branch?) (conj parent-ids next-id)
(not (z/right loc)) (some-> parent-ids not-empty pop)
:else parent-ids)
(conj acc
{:id (str next-id)
:label (str (z/node loc))
:parent (when (seq parent-ids)
(str (peek parent-ids)))}))
:else
(recur (z/next loc) next-id parent-ids acc))))
loop
具有以下绑定:
loc
版本:id
值,每次我们看到叶子节点时都会增加:id
的:parent
值位于parent-ids
堆栈的顶部。acc
累加器向量您可以使用拉链调用该函数:
(index-zipper (z/seq-zip '(a (b (c d)) (e (f)))))
=>
[{:id "0", :label "a", :parent nil}
{:id "1", :label "b", :parent "0"}
{:id "2", :label "c", :parent "1"}
{:id "3", :label "d", :parent "1"}
{:id "4", :label "e", :parent "0"}
{:id "5", :label "f", :parent "4"}]
(index-zipper (z/seq-zip '(a (a (a a)) (a (a)))))
=>
[{:id "0", :label "a", :parent nil}
{:id "1", :label "a", :parent "0"}
{:id "2", :label "a", :parent "1"}
{:id "3", :label "a", :parent "1"}
{:id "4", :label "a", :parent "0"}
{:id "5", :label "a", :parent "4"}]
答案 1 :(得分:-1)
这是一种方法。您只需要添加有关为每个节点分配“ id”的部分。
还请注意,您应该重新设置输入数据的格式,以使每个节点都为打ic格式(即,即使没有孩子的单例节点也被包裹在向量中)。
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require [tupelo.core :as t]))
(def tree
[:a
[:b
[:c]
[:d]]
[:e
[:f]]])
(def relationships (atom []))
(defn save-relationships
[parent-id curr-node]
(let [curr-id (first curr-node)
kid-nodes (rest curr-node)]
(swap! relationships #(conj % {:parent parent-id :label curr-id}))
(doseq [kid-node kid-nodes]
(save-relationships curr-id kid-node))))
(dotest
(reset! relationships [])
(save-relationships nil tree)
(spyx-pretty @relationships))
结果:
~/expr/demo >
~/expr/demo > lein test
lein test _bootstrap
-------------------------------
Clojure 1.10.1 Java 12
-------------------------------
lein test tst.demo.core
(clojure.core/deref relationships) =>
[{:parent nil, :label :a}
{:parent :a, :label :b}
{:parent :b, :label :c}
{:parent :b, :label :d}
{:parent :a, :label :e}
{:parent :e, :label :f}]
Ran 2 tests containing 0 assertions.
0 failures, 0 errors.