Нагруженный с функциональным пересечением дерева по ширине в Clojure?
Скажем, у меня есть дерево, определенное в соответствии с рекомендацией в этом сообщении, хотя это вектор в моем случае, который, надеюсь, не должен иметь значения (они являются векторами в Программе Clojure):
(def tree [1 [[2 [4] [5]] [3 [6]]]])
который должен выглядеть примерно так:
1
/ \
2 3
/ \ |
4 5 6
Теперь я хотел бы сделать обход ширины в дереве без каких-либо традиционных средств, таких как очередь, и вместо этого использовать исключительно стек для передачи информации. Я знаю, что это не самый простой маршрут, но я делаю это в основном как упражнение. Также на этом этапе я не планирую возвращать коллекцию (я потом узнаю об этом как упражнение), но вместо этого просто распечатаю узлы, когда я путешествую через них.
Мое текущее решение (только начинать с Clojure, быть хорошим):
(defn breadth-recur
[queue]
(if (empty? queue)
(println "Done!")
(let [collections (first (filter coll? queue))]
(do
; print out nodes on the current level, they will not be wrapped'
; in a [] vector and thus coll? will return false
(doseq [node queue] (if (not (coll? node)) (println node)))
(recur (reduce conj (first collections) (rest collections)))))))
Последняя строка не работает должным образом, и я не понимаю, как ее исправить. Я точно знаю, что хочу: мне нужно очистить каждый слой векторов, а затем объединить результаты, чтобы перейти в recur.
Проблема, которую я вижу, в основном:
IllegalArgumentException Don't know how to create ISeq from: java.lang.Long
В принципе conj не нравится добавлять вектор к длинному, и если я сменю conj для concat, то я терпит неудачу, когда один из двух элементов, конкатенирование не является вектором. И кон, и concat терпят неудачу при столкновении:
[2 [4] [5] [3 [6]]]
Мне кажется, что мне не хватает действительно основной операции здесь, которая будет работать как на векторах, так и на примитивах в обеих позициях.
Любые предложения?
Изменить 1:
На самом деле дерево должно быть (спасибо Joost!):
(def tree [1 [2 [4] [5]] [3 [6]]])
Однако мы еще не нашли ширину - первое решение.
Ответы
Ответ 1
Поскольку, по-видимому, до сих пор не найдено ни одного расширенного решения, вот простой алгоритм, реализованный с первого взгляда, а затем преобразованный в ленивый:
(defn bfs-eager [tree]
(loop [ret [], queue (conj clojure.lang.PersistentQueue/EMPTY tree)]
(if (seq queue)
(let [[node & children] (peek queue)]
(recur (conj ret node) (into (pop queue) children)))
ret)))
(defn bfs-lazy [tree]
((fn step [queue]
(lazy-seq
(when (seq queue)
(let [[node & children] (peek queue)]
(cons node
(step (into (pop queue) children)))))))
(conj clojure.lang.PersistentQueue/EMPTY tree)))
Ответ 2
Данные дерева неверны. Это должно быть [1 [2 [4] [5]] [3 [6]]]
Кроме того, вы смешиваете обход дерева с печатью и создаете результат. Все становится проще, если вы концентрируетесь на выполнении жесткой части отдельно:
(def tree [1 [2 [4] [5]] [3 [6]]])
ПРИМЕЧАНИЕ. ЭТО ГЛУБИНА-ПЕРВАЯ. СМОТРЕТЬ НИЖЕ
(defn bf "return elements in tree, breath-first"
[[el left right]] ;; a tree is a seq of one element,
;; followed by left and right child trees
(if el
(concat [el] (bf left) (bf right))))
(bf tree)
=> (1 2 4 5 3 6)
ПРАВИЛЬНАЯ ВЕРСИЯ
(defn bf [& roots]
(if (seq roots)
(concat (map first roots) ;; values in roots
(apply bf (mapcat rest roots))))) ;; recursively for children
(bf tree)
=> (1 2 3 4 5 6)
Ответ 3
Это может помочь, я создавал алгоритм для оценки того, является ли дерево симметричным и использует обход ширины:
(defn node-values [nodes]
(map first nodes))
(defn node-children [nodes]
(mapcat next nodes))
(defn depth-traversal [nodes]
(if (not (empty? nodes))
(cons (node-values nodes) (depth-traversal (node-children nodes)))))
(defn tree-symmetric? [tree]
(every?
(fn [depth] (= depth (reverse depth)))
(depth-traversal (list tree))))
(def tree '(1 (2 (3) (4)) (2 (4) (3))))
(node-values (list tree)) ; (1)
(node-children (list tree)) ; ((2 (3) (4)) (2 (4) (3)))
(depth-traversal (list tree)) ; ((1) (2 2) (3 4 4 3))
(tree-symmetric? tree) ; true
Ответ 4
многие комбинации reduce
и conj
могут быть заменены одним вызовом into
в приведенном выше случае с уменьшением вам может потребоваться передать начальный пустой вектор, чтобы уменьшить до
Получайте союз счастливым.