Clojure:如何生成';trie';?
鉴于以下情况Clojure:如何生成';trie';?,clojure,tree,functional-programming,trie,Clojure,Tree,Functional Programming,Trie,鉴于以下情况 (def inTree '((1 2) (1 2 3) (1 2 4 5 9) (1 2 4 10 15) (1 2 4 20 25))) 您将如何将其转换为此trie (def outTrie '(1 (2 () (3 ()) (4 (5 (9 ())) (10 (15 ())) (20 (25 ()))
(def inTree
'((1 2)
(1 2 3)
(1 2 4 5 9)
(1 2 4 10 15)
(1 2 4 20 25)))
您将如何将其转换为此trie
(def outTrie
'(1
(2 ()
(3 ())
(4 (5
(9 ()))
(10
(15 ()))
(20
(25 ()))))))
作为一般方法,我将做以下几点:
- 编写一些函数来创建trie并将新元素插入trie
- 创建一个新的trie
- 迭代输入列表并将每个元素插入到trie中
这个问题非常适合递归实现。如果可能的话,我会尽量做到这一点。我相信有一种更漂亮的方式(有!请看Brian的答案,它更好): ->(无(1(2)(4)(20(25))(10(15))(5(9))(3)))
请注意,我选择使用nil作为根节点,并且没有费心保留空列表来表示没有子节点。实际上,这样做是不正确的,因为它不保留子字符串标识。这里的列表非常笨拙,更不用说效率低下了。在Clojure中,更习惯于在适当的时候使用向量、哈希映射和集合。使用哈希映射:
(def in-tree
'((1 2)
(1 2 3)
(1 2 4 5 9)
(1 2 4 10 15)
(1 2 4 20 25)))
(defn add-to-trie [trie x]
(assoc-in trie `(~@x :terminal) true))
(defn in-trie? [trie x]
(get-in trie `(~@x :terminal)))
如果要打印排序的地图,可以使用排序地图
s,但必须在中编写自己的assoc版本,该版本一直使用排序地图。无论如何:
user> (def trie (reduce add-to-trie {} in-tree))
#'user/trie
user> trie
{1 {2 {4 {20 {25 {:terminal true}}, 10 {15 {:terminal true}}, 5 {9 {:terminal true}}}, 3 {:terminal true}, :terminal true}}}
user> (in-trie? trie '(1 2))
true
user> (in-trie? trie '(1 2 4))
nil
user> (in-trie? trie '(1 2 4 20 25))
true
这是一个干净的解决方案。这修复了Brian的AddtoTrie方法的一个错误,因为它当前依赖于您以递增的长度顺序插入seq。它还允许通过前缀查询trie,这是一个常见的用例
注意,这里的内存使用率更高,因为它将值存储在trie的叶节点中,因此您可以执行搜索
(defn add-to-trie [trie x]
(assoc-in trie x (merge (get-in trie x) {:val x :terminal true})))
(defn in-trie? [trie x]
"Returns true if the value x exists in the specified trie."
(:terminal (get-in trie x) false))
(defn prefix-matches [trie prefix]
"Returns a list of matches with the prefix specified in the trie specified."
(keep :val (tree-seq map? vals (get-in trie prefix))))
(defn build-trie [coll]
"Builds a trie over the values in the specified seq coll."
(reduce add-to-trie {} coll))
谢谢查看常见问题的代码有助于发现语言的惯用语。不用担心,请参阅Brian的答案。它更惯用、更正确。这是一个很好的答案,并强调我的答案实际上错误地忽略了子字符串问题。我建议在tri?中使用一个稍有不同的方法:(defn in trie?[trie x](:terminal(get in trie x)false))user=>(in trie?trie'(1 2 4))false使其成为一个真正的谓词,并避免拼接语法。也许::terminal
,以防我们在序列中使用:terminal
,我已经修复了发现的错误。请随意反向编辑。与@TimothyPratley不同,我发现您使用无引号拼接很有帮助,因为它显示了对数据的操作,而不是被困在宏体中。因此,如果您总是使用相同数量的键,我想Brian的版本会很好?您对前缀匹配的定义使用了函数映射过滤器,但是标准库中没有这样的函数。我试图对它的功能进行反向工程,但并不明显。你能发布它的定义吗?map filter
类似于核心库中的keep
。我已经为你提到的bug应用了最小修复。
(defn add-to-trie [trie x]
(assoc-in trie x (merge (get-in trie x) {:val x :terminal true})))
(defn in-trie? [trie x]
"Returns true if the value x exists in the specified trie."
(:terminal (get-in trie x) false))
(defn prefix-matches [trie prefix]
"Returns a list of matches with the prefix specified in the trie specified."
(keep :val (tree-seq map? vals (get-in trie prefix))))
(defn build-trie [coll]
"Builds a trie over the values in the specified seq coll."
(reduce add-to-trie {} coll))