Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure:如何生成';trie';?_Clojure_Tree_Functional Programming_Trie - Fatal编程技术网

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))