Функция пути застежек-молний Clojure не завершена?

EDIT #2: Весь этот вопрос и исследование были основаны на том, что я упустил из виду фундаментальное понятие молнии; что они представляют перспективу в структуре данных с точки зрения конкретного узла. Таким образом, застежка-молния всегда является парой текущего узла и того, как выглядит остальная часть дерева с точки зрения этого узла. Изначально я пытался сгенерировать совершенно новую структуру из застежки-молнии, в то время как сама застежка-молния была всем, что мне было нужно. Я оставлю все это для потомков, в надежде, что это поможет кому-то еще (по крайней мере, это служит предупреждением для любых преемников!).

Оригинальный вопрос:

Я пытаюсь понять, как использовать молнии для управления деревьями. Конкретная проблема заключается в том, что мне нужно генерировать во время выполнения маршруты между двумя узлами, соответствующими произвольным критериям в произвольном дереве.

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

Например:

(def test-zip (vector-zip [0 [1] [2 [3 [4] 5 [6 [7] [8]]]]]))
(-> test-zip 
    down right right down 
    right down right right
    node)

дает 5, но

(-> test-zip 
    down right right down 
    right down right right
    path)

дает

[[0 [1] [2 [3 [4] 5 [6 [7] [8]]]]] [2 [3 [4] 5 [6 [7] [8]]]] [3 [4] 5 [6 [7] [8]]]]

это не то же место (отсутствует эффект последних трех шагов, down right right).

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

Я упускаю смысл функции path? Я предполагал, что при наличии дерева и пути применение пути к дереву приведет вас к исходному местоположению пути, а не частично туда.

ОБНОВЛЕНИЕ: я использовал следующее определение функции для компиляции пути узлов от начального до конечного местоположения:

(defn lca-path [start-loc end-loc]
  (let [sczip (z/root start-loc)
        start-path (z/path start-loc)
        start-node (z/node start-loc)
        end-path (z/path end-loc)
        end-node (z/node end-loc)
        lca-path (filter (set start-path) end-path)
        lca-node [(last lca-path)]
        lca-to-start (conj (vec (drop (count lca-path) start-path)) start-node)
        lca-to-end (conj (vec (drop (count lca-path) end-path)) end-node)
        ]

    (concat (reverse lca-to-start) lca-node lca-to-end))
  )

Очень сильно повлиял чат с @Mark Fisher, спасибо!


person Oliver Mooney    schedule 08.05.2015    source источник


Ответы (1)


Я думаю, вы правы, это путь к родителю, и это подтверждается взглядом на этом сайте, path возвращает "все поддеревья от корня дерева до чуть выше текущего местоположения".

Для вашей структуры дерево:

   A
 / | \
0  o  B
   | / \
   1 2  C __
       /|\  \
      3 o 5  o
        |   /|\
        4  6 o o
             | |
             7 8

Таким образом, 3 узла, отмеченные A, B, C, являются родительскими узлами, которые приводят к родителю 5, а именно:

A: [0 [1] [2 [3 [4] 5 [6 [7] [8]]]]]
B: [2 [3 [4] 5 [6 [7] [8]]]]
C: [3 [4] 5 [6 [7] [8]]]

какой результат вы получаете.

РЕДАКТИРОВАТЬ: я создал эту функцию для поиска между двумя значениями в векторе и возврата path между ними. Это помогает? Я не эксперт по застежкам-молниям, так что это тоже учит меня.

(defn between [v s e]
  (let [zv (zip/vector-zip v)
        find-loc (fn [zv l]
                   (loop [loc zv]
                     (if (= l (zip/node loc))
                       loc 
                       (if (zip/end? loc)
                           nil
                           (recur (zip/next loc))))))
        sv (zip/vector-zip (-> (find-loc zv s) zip/up zip/node))
        e-in-sv (find-loc sv e)
        path-s-e (when (some? e-in-sv)
                   (zip/path e-in-sv))]
    path-s-e))

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

(def sov [0 [1] [2 [3 [4] 5 [6 [7] [8]]]]])
(between sov 2 6)
=> [[2 [3 [4] 5 [6 [7] [8]]]] [3 [4] 5 [6 [7] [8]]] [6 [7] [8]]]

Я иду по дереву, чтобы найти родителя начального значения, а затем создаю новую молнию из его родителя, чтобы путь был ограничен от начальной точки. Вспомогательная функция «find-loc» возвращает застежку-молнию для записи, которую вы ищете, например. "5" внутри вектора. Тогда sv является родительским вектором, содержащим начальное значение.

Там, где нет пути, он возвращает ноль, например. между 1 и 4 вам придется пройти 2 элемента.

Это было в ответ на ваши комментарии о необходимости найти значение в конечном узле (например, 5 в [3 [4] 5 [6 [7] [8]]]), что, я думаю, вам не нужно делать, но без реального примера того, что вы делаете, трудно комментировать на.

Кстати, могут быть гораздо лучшие способы обхода/поиска значений в векторе, чем пользовательская функция, но, как я уже сказал, застежки-молнии для меня тоже довольно новы.

person Mark Fisher    schedule 08.05.2015
comment
Значит, как только вы доберетесь до родительского поддерева, вам придется вручную искать нужный узел в непосредственных потомках последнего поддерева? Учитывая, что сразу нисходящие узлы 3. [4], 5 и [6 [7] [8]] в данном случае это не слишком широкий поиск. Но не странно ли, что для всех четырех локаций возвращается один и тот же путь? Есть ли какой-нибудь другой идиоматический способ зафиксировать маршрут к месту в молнии, которую мне не хватает? - person Oliver Mooney; 08.05.2015
comment
Я как бы делюсь этим путешествием с вами. Я обновил свой ответ, включив в него искатель вашего вектора между двумя точками. Соответствует ли это тому, чего вы пытаетесь достичь? - person Mark Fisher; 08.05.2015
comment
Также я думаю, что это так, потому что вы используете простые векторные молнии, а не какую-то пользовательскую молнию для ваших данных. - person Mark Fisher; 08.05.2015
comment
Разве это не дублирует функциональность функции path? Как только вы найдете начальное и конечное местоположение, а также путь вызова для обоих из них, вы можете выполнить (filter (set start-path) end-path), чтобы получить наименьшего общего предка обеих точек. Я также делаю lca-to-start-parent (drop (count lca-loc) start-path), чтобы получить путь от низшего общего предка до начальной точки, и (drop (count lca-loc) end-path) для другой ветви до конечной точки. Мне нравится ваша автономная реализация, и как вы могли бы использовать идентичную? для перехода к родительскому местоположению... - person Oliver Mooney; 08.05.2015
comment
Только что увидел ваш следующий комментарий - на самом деле я использую векторы в качестве упрощенного примера, чтобы разобраться с застежками-молниями, я использую пользовательскую застежку-молнию для своих фактических данных, но хотел исключить ее как усложняющий фактор. - person Oliver Mooney; 08.05.2015
comment
возможно да! Я пытался лучше понять вашу проблему и решил поделиться. в программировании на Clojure есть хороший раздел о переходе между двумя точками в лабиринте с использованием специальной застежки-молнии с использованием пути (clojurebook.com). Я думаю, что природа вашего вопроса о том, что один и тот же путь к одному и тому же количеству элементов, является скорее свойством использования векторных застежек-молний, ​​а не общей проблемой с застежками-молниями.... и этот комментарий пересекается с вашим комментарием :) - person Mark Fisher; 08.05.2015
comment
Вероятно, стоит также отметить, что мы оба принимаем уникальные значения для узлов (верно в моем случае) - person Oliver Mooney; 08.05.2015