Clojure의 트리 데이터 구조에서 Spectre 사용
15166 단어 treeclojurescriptalgorithmsclojure
자세히 살펴보면 마인드 매핑 구조가 트리 데이터 구조라는 것을 즉시 알 수 있습니다. 즉, 깊이 중첩된 구조를 처리해야 하고 작업에 적합한 도구가 필요합니다. 가장 먼저 떠오르는 것은 Specter 이라는 멋진 라이브러리입니다.
더 이상 고민하지 않고 몇 가지 코드를 확인하겠습니다. (프로젝트에서 제공한 코드 샘플)
CRUD 기능 중 하나는 id로 노드를 찾는 것이었습니다. 그래서 저는 이것을 생각해 냈습니다.
(require '[com.rpl.specter :as s])
(defn find-node-by-id [tree id]
(s/select-first (s/walker #(= id (:id %))) tree))
Walker는 pred 함수가 진실 값을 반환하는 노드에 대해 깊이 우선 탐색을 실행합니다. pred가 참 값을 반환하면 Walker는 트리의 해당 분기 검색을 중지하고 나머지 데이터 구조 검색을 계속합니다.
모든 노드에는 고유한 id 속성이 있으므로
select-first
매크로를 사용하여 첫 번째 노드를 선택할 수 있습니다.또한 자식 ID로 부모 노드를 찾아야 했습니다.
(defn find-parent-by-child-id [tree child-id]
(s/select-first
(s/walker
(fn [node]
((set (map :id (:children node))) child-id)))
tree))
언젠가는 같은 수준의 노드를 찾아야 합니다.
(defn find-same-level-nodes [tree node-id]
(let [parent (find-parent-by-child-id tree node-id)]
(if (:root? parent)
(:children parent)
(->> parent
:id
(find-parent-by-child-id tree)
:children
(mapcat :children)))))
트리에서 일부 노드를 제거하고 업데이트해 보겠습니다. 그렇게 하기 전에 트리를 순회하기 위해
recursive-path
를 정의할 수 있습니다. (또 다른 접근 방식)(def MAP-NODES
(s/recursive-path [] p
(s/cond-path
sequential? (s/continue-then-stay s/ALL p)
map? (s/continue-then-stay s/MAP-VALS p))))
Spectre는 순차적이거나 지도인 데이터를 발견하면 MAP-VALS에 대한 데이터 검색으로 재귀적으로 이동합니다. (우리는
coll?
만 사용할 수도 있습니다)자, 이 중요한 경로를 정의한 후 노드를 추가해 보겠습니다.
(defn add-node [tree parent-id node id]
(let [node (assoc node :id id :h shape-h :w shape-w)
tree (s/transform [q/MAP-NODES #(= parent-id (:id %)) :children]
#((fnil conj []) % node)
tree)]
(update-positions tree parent-id id)))
add-node
는 MAP-NODES 를 사용하여 트리를 탐색하고 주어진 parent-id
를 찾으면 제공된 노드가 :children
에 추가됩니다.제거 작업;
(defn remove-node-by-id [tree id]
(s/setval [MAP-NODES #(= id (:id %))] s/NONE tree))
여기에서는
setval
매크로를 사용하여 노드를 제거합니다.업데이트도 비슷합니다.
(defn update-node-by-id [tree id update-fn]
(s/transform [MAP-NODES #(= id (:id %))] update-fn tree))
업데이트 기능 때문에
transform
대신 setval
를 사용했습니다.중첩된 모든 자식을 부모 ID로 업데이트합시다. 먼저 모든 노드를 가져오는 함수를 만들어 보겠습니다.
(defn- traverse [node]
(when (-> node :children seq)
(lazy-cat (:children node) (map traverse (:children node)))))
(defn get-nodes [root]
(->> root
(traverse)
(cons root)
(flatten)
(remove nil?)))
이제 모든 자식에게 업데이트 작업을 적용할 수 있습니다.
(defn update-children-by-parent-id [tree parent-id update-fn]
(let [parent (find-node-by-id tree parent-id)
children (set (map :id (rest (get-nodes parent))))]
(s/transform [MAP-NODES #(children (:id %))] update-fn tree)))
포스팅을 너무 길게 하고 싶지 않습니다. 나는 당신이 전체 그림을 가지고 있다고 생각합니다.
간단히 말해서 Spectre는 강력하고 재미있게 작업할 수 있습니다. 잘 구조화된 추상화로 상당히 복잡한 문제를 해결할 수 있습니다.
저와 비슷한 사용 사례가 있다면 적극 추천합니다.
Reference
이 문제에 관하여(Clojure의 트리 데이터 구조에서 Spectre 사용), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/ertugrulcetin/using-specter-on-tree-data-structures-in-clojure-2l15텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)