Clojure молния, чтобы удалить все правильные родные братья - PullRequest
1 голос
/ 26 марта 2019

Я хочу написать функцию для молнии, которая удаляет всех правильных братьев и сестер узла, оставаясь в том же месте.

(defn remove-all-rights-1 [loc]
  (if (zip/right loc) 
    (recur (zip/remove (zip/right loc)))
    loc))

Проблема в том, что remove возвращает местоположение, которое предшествовало бы текущему узлу в DFS.

Поэтому следующий пример ...

(-> (clojure.zip/vector-zip [1 [[2] 3]])
    (zip/down)
    (zip/right)
    (zip/down)
    (remove-all-rights-1)
    (zip/replace :x)
    (zip/root))

... дает [1 [[:x]]] вместо [1 [:x]], потому что zip/remove прыгнул на нижний лист вместо того, чтобы просто вернуться назад влево.

Как мне удалить правильных братьев и сестер без также изменения местоположения в дереве ? Заранее спасибо!

Ответы [ 3 ]

1 голос
/ 26 марта 2019

Обобщая ответ Аконда дал следующее решение:

(defn remove-all-rights
  "Removes all right siblings. Stays at original location."
  [loc]
  (let [parent-loc (zip/up loc)
        |lefts| (inc (count (zip/lefts loc)))]
    (->> (zip/make-node loc (zip/node parent-loc) (take |lefts| (zip/children parent-loc)))
         (zip/replace parent-loc)
         (zip/down)
         (zip/rightmost))))

Основная идея состоит в том, чтобы создать копию родительского узла, в котором коллекция дочерних элементов не содержит правильных братьев и сестер.

1 голос
/ 26 марта 2019

Это можно легко сделать с помощью библиотеки Tupelo Forest:

(dotest
  (with-forest (new-forest)
    (let [edn-orig          [1 [[2] 3]]
          root-hid          (add-tree (edn->tree edn-orig))
          hid               (find-hid root-hid [::tf/list ::tf/list])
          subtree-edn-orig  (-> hid hid->tree tree->edn)
          >>                (kids-update hid butlast)
          subtree-edn-final (-> hid hid->tree tree->edn)
          edn-final         (-> root-hid hid->tree tree->edn)]

      (is= subtree-edn-orig      [[2] 3])
      (is= subtree-edn-final     [[2]])
      (is= edn-final          [1 [[2]]]  ))))

В созданном дереве есть узлы со :tag значениями :tupelo.forest/list на первом и втором уровнях:

  (is= (hid->bush root-hid)
    [{:tag :tupelo.forest/list, :tupelo.forest/index nil}
     [#:tupelo.forest{:value 1, :index 0}]
     [{:tag :tupelo.forest/list, :tupelo.forest/index 1}
      [{:tag :tupelo.forest/list, :tupelo.forest/index 0}
       [#:tupelo.forest{:value 2, :index 0}]]]] )

HID - это указатель на узел дерева, поэтому root-hid указывает на корневой узел дерева, а hid указывает на поддерево [[2] 3]. После удаления самого правого узла 3, hid указывает на поддерево [[2]].

Для дочерних узлов (kids) мы используем функцию butlast, чтобы удалить самые правые, а затем преобразовать данные из формата леса / дерева обратно в EDN.

См. README здесь , а API документы здесь . Здесь также есть много примеров реального кода . Также смотрите Clojure Conj видео .

1 голос
/ 26 марта 2019
(letfn [(kill-right [loc]
            (let [lost   (zip/rights loc)
                  parent (-> loc zip/up zip/node)
                  node   (into (empty parent) (take (- (count parent) (count lost)) parent))]
                (-> loc
                    zip/up
                    (zip/replace node)
                    zip/down
                    zip/rightmost)))]
    (-> (clojure.zip/vector-zip [1 [[2] 3]])
        zip/down
        zip/right
        zip/down
        kill-right
        (zip/replace :x)
        zip/root))
...