Clojure,在Okasaki使用recur

时间:2011-11-27 12:40:39

标签: clojure refactoring

我天真地在Clojure的Okasaki的书“Purely Functional Data Structures”中实现了这个特里。仅仅是为了学习目的,因为可能没有真正的收获,因为Clojure在法线贴图的引擎下使用了更有效的特里,对吧?有一件事情仍困扰着我:trie-assoc不使用recur。我怎么能重构它所以它只使用recur对尾部进行递归调用?我觉得我可以使用拉链,但我还没有看过拉链。 可能可以改进的另一件事是(if(空?键)......)然后是键的解构。这会更简洁吗?

(ns okasaki-trie.core)

(def empty-trie {:value nil :nodes nil})

(defn trie-get [key trie]
  (if (empty? key) (get trie :value)
      (let [[k & ks] key]
        (recur ks (get-in trie [:nodes k])))))

(defn trie-assoc [key val trie]
  (if (empty? key) (assoc trie :value val) 
      (let [[k & ks] key
            sub-trie-k (or (get-in trie [:nodes k]) empty-trie)
            sub-trie-k-new (trie-assoc ks val sub-trie-k)]
        (assoc-in trie [:nodes k] sub-trie-k-new))))

;;(def first-trie (trie-assoc "a" 3 empty-trie))
;;first-trie => {:nodes {\a {:nodes nil, :value 3}}, :value nil}

;;(def second-trie (trie-assoc "ab" 5 first-trie))
;;second-trie => {:nodes {\a {:nodes {\b {:nodes nil, :value 5}},
;;:value 3}}, :value nil}

;;(def third-trie (trie-assoc "b" 2 second-trie))
;;third-trie  => {:nodes {\b {:nodes nil, :value 2}, \a {:nodes {\b
;;{:nodes nil, :value 5}}, :value 3}}, :value nil}

;;(trie-get "b" third-trie)  ;=> 2
;;(trie-get "ab" third-trie) ;=> 5
;;(trie-get "abc" third-trie)  ;=> nil

2 个答案:

答案 0 :(得分:4)

我没有深入研究这个算法,但我怀疑尾递归是否(有用)可行。您必须沿着树走下去,然后将其备份以重建到根路径上的节点。这需要一个堆栈,因此没有recur

如果需要,可以通过保留父节点列表并手动将它们移回根节点来管理堆上自己的“堆栈”。然后你可以使用recur并避免堆栈溢出,但你不会使用任何更少的内存(可能更多,真的)。所以这只是一个有用的技巧,如果你的树中有2 ^ 1000个元素(我很怀疑你这样做)。

修改

刚刚注意到你的问题

(if (empty? key)
  (...empty...)
  (let [[k & ks] key]
    (...more...)))

您可以更简洁,更简洁地重写:

(if-let [[k & ks] (seq key)]
  (...more...)
  (...empty...))

答案 1 :(得分:0)

我知道这既是死灵法术,又有点偏离主题,但我遇到了这个问题探索在Clojure中实现trie。我不得不通过捆绑来实现不变性。这是我提出的解决方案:

(def  trie (atom {}))
(defn key-list     [s    ] (into [] (map str s)))
(defn trie-add     [t k v] (assoc-in t (conj (key-list k) :v) v))
(defn trie-query   [t k  ] (get-in (deref t) (conj (key-list k) :v)))
(defn trie-subtree [t k  ] (get-in (deref t) (key-list k)))

提供了一个令人惊讶的干净实现,所以我想分享。

用于剪切和粘贴探索:

(swap! trie trie-add "aac" 1)
(swap! trie trie-add "abc" 4)
(swap! trie trie-add "ab" 5)
(swap! trie trie-add "oaailfnalieuhkakjdfkjh" 1)
(swap! trie trie-add "aaailfnalieuhkakjdfkjh" 1)
(swap! trie trie-add "oaailfnalieancbnwiuybw" 1)
(swap! trie trie-add "oa" 3)

(trie-query trie "ab")
(trie-query trie "abc")
(trie-query trie "oaailfnalie")
(trie-query trie "oa")
(trie-query trie "oaailfnalieuhkakjdfkjh")

(trie-subtree trie "oa")
(trie-subtree trie "a")
(trie-subtree trie "oaailfnalieancbnwiuyb")
(trie-subtree trie "oaailfnalieancbnwiuy")
(trie-subtree trie "oaailfnalieancbnwiu")

(deref trie)
好时光。也许这回答了“这可能是更简洁”的问题。