问题描述
我有一棵这样的树:
let problemSet = [problemA,problemB,...]
let shuffledProblemSet = shuffle(problemSet) // fisher-yates shuffle
// to present a "random" non-repeating problem,just traverse the shuffled problems in order
let index = 0
let nextProblem = shuffledProblemSet[index++]
// Now,send this to discord...
const embed = new discord.MessageEmbed()
.addField(nextProblem.question,invisible) //Here I put a random question
.addField(nextProblem.answers) // And here I would want to put the appropriate answer choices
// yada yada
如何获得边缘,即:
[:root
[:a [:b [:c [:g]]]]
[:d [:e [:f [:g]]]]]
解决方法
这是我在检查您的答案之前想到的。除非我错过了一些东西,否则似乎更惯用了。
(defn vec->edges [v-tree]
(->> v-tree
(tree-seq vector? next)
(mapcat (fn [[a & children]]
(map (fn [[b]] [a b]) children)))))
,
此方法使用一个基本循环(不需要额外的库或递归):
(defn get-edges [tree]
(loop [subtrees [tree]
output []]
(if (empty? subtrees)
output
(let [[[root & first-subtrees] & subtrees] subtrees]
(recur (into subtrees first-subtrees)
(into output (map #(-> [root (first %)])) first-subtrees))))))
在示例数据上对其进行测试:
(get-edges [:root
[:a [:b [:c [:g]]]]
[:d [:e [:f [:g]]]]])
;; => [[:root :a] [:root :d] [:d :e] [:e :f] [:f :g] [:a :b] [:b :c] [:c :g]]
这是另一种基于延迟序列的方法:
(defn get-edges2 [tree]
(->> [tree]
(iterate #(into (rest %) (rest (first %))))
(take-while seq)
(mapcat (fn [subtrees]
(let [[[root & sub] & _] subtrees]
(map #(-> [root (first %)]) sub))))))
,
我真的很喜欢您提出问题的方式Scott Klarenbach,这真的是综合性的。
我提出了一种解决方案,解决原始的Clojure问题。棘手的部分是递归调用的位置以及如何处理这些递归调用的结果。
(def data
[:root
[:a [:b [:c [:g]]]]
[:d [:e [:f [:g]]]]])
(defn get-edges [collection]
(let [root (first collection)
branches (rest collection)]
(if (empty? branches)
[]
(let [edges
(mapv (fn [branch] [root (first branch)]) branches)
sub-edges
(->> branches
(mapcat (fn [branch] (get-edges branch)))
vec)]
(if (empty? sub-edges)
edges
(vec (concat edges sub-edges)))))))
(get-edges data)
;; => [[:root :a] [:root :d] [:a :b] [:b :c] [:c :g] [:d :e] [:e :f] [:f :g]]
,
没有简单的内置方法可以做到这一点。最简单的方法是滚动自己的递归。
这是具有唯一打h标签(无重复)的解决方案:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require
[schema.core :as s]))
(def result (atom []))
(s/defn walk-with-path-impl
[path :- [s/Any] ; parent path is a vector
user-fn
data :- [s/Any]] ; each data item is a hiccup vector
(let [tag (xfirst data)
path-next (append path tag)]
(user-fn path data)
(doseq [item (xrest data)]
(walk-with-path-impl path-next user-fn item))))
(defn walk-with-path!
[user-fn data]
(walk-with-path-impl [] user-fn data))
(s/defn print-edge
[path :- [s/Any] ; parent path is a vector
data :- [s/Any]] ; each data item is a hiccup vector
(when (not-empty? path)
(let [parent-node (xlast path)
tag (xfirst data)
edge [parent-node tag]]
(swap! result append edge))))
单元测试显示输入数据和结果
(dotest
(let [tree [:root
[:a
[:b
[:c
[:z]]]]
[:d
[:e
[:f
[:g]]]]]]
(reset! result [])
(walk-with-path! print-edge tree)
(is= @result
[[:root :a] [:a :b] [:b :c] [:c :z]
[:root :d] [:d :e] [:e :f] [:f :g]])))
Here are the docs用于便捷功能,例如xfirst
,append
等