反序列化广度优先树

时间:2015-12-31 11:37:51

标签: algorithm clojure tree

我需要按照以下方式使用四叉树:

           p
         /| |\ 
        / | | \    
       /  | |  \
      p   w b   p
    /| |\     /| |\   
   / | | \   / | | \
  b  w w  b w  b w  b

但是它们已经使用广度优先顺序序列化为字符串,因此前一个树将具有以下表示:

ppwbpbwwbwbwb

我试图将这样的字符串转换为嵌套的矢量结构:

[ [ b w w b ] w b [ w b w b] ]

但有时以下代码无法正常工作:

(defn read-quad-tree [pattern]
  (loop [r                    []
         [s & rs :as stack]   []
         [p & rp :as pending] (reverse pattern)]
    (cond (nil? pending)  (first r)
          (= (count r) 4) (recur [] (conj stack (reverse r)) pending)
          (= p \p)        (recur (conj r s) rs rp)
          :else           (recur (conj r p) stack rp))))

编辑添加一个复杂的示例:

另一个(失败的)例子。下一棵树:

                    |
         +------+-------+------+
         |      |       |      |
         |      w       w      |
         |                     |
   +---+---+---+         +---+---+---+
   |   |   |   |         |   |   |   | 
   |   w   w   |         |   w   w   |
   |           |         |           |
+-+-+-+     +-+-+-+   +-+-+-+     +-+-+-+
| | | |     | | | |   | | | |     | | | |
b b b b     w w w w   b b w w     b w b w

将序列化为:

ppwwppwwppwwpbbbbwwwwbbwwbwbw

目标是获得以下结构:

[ [ [ b b b b ] w w [ w w w w ] ] w w [ [ b b w w ] w w [ b w b w ] ] ]

但是我的代码给出了一个不同的(错误的)结构。

1 个答案:

答案 0 :(得分:2)

您的代码会遇到一些代表列表而不是(预期)向量的名称。

这已经从函数的返回值中看到了,这是一个列表。

查看代码的这些部分:

(loop   ...
        [s & rs :as stack]
        ...
        (recur (conj r s) rs rp)

突出显示的[s & rs :as stack]将获取堆栈向量,将第一个元素命名为 s ,其余元素命名为 list (! ) rs 。由于 rs 是一个列表,它会在某些时候传递给 stack (通过最后的recur),然后它也是一个列表,并且不是矢量。然后,下次 r 有4个元素时,(conj stack ...将不会附加 r ,而是添加前缀,因为这是conj的行为适用于列表。引自docs

  

;;请注意,与矢量结合在最后完成

     

;;联系到列表的通知在开头就完成了

当然,这会破坏预期的算法,并解释你得到的错误结果。

虽然 r 是一个向量,但(reverse r) returns a seq会出现类似的问题。

例如,您可以通过将(into [] ...)应用于要将列表作为向量传递的位置来修复它。我看到你需要做的两个地方:

(defn read-quad-tree [pattern]
  (loop [r                    []
         [s & rs :as stack]   []
         [p & rp :as pending] (reverse pattern)]
    (cond (nil? pending)  (first r)
          (= (count r) 4) (recur [] (conj stack (into [] (reverse r))) pending)
          (= p \p)        (recur (conj r s) (into [] rs) rp)
          :else           (recur (conj r p) stack rp))))

pending 不需要修复,因为它永远不会“感染”列表类型的其他名称。

当像这样调用纠正的代码时:

(println (read-quad-tree "ppwwppwwppwwpbbbbwwwwbbwwbwbw"))

它会输出:

[[[b b b b] w w [w w w w]] w w [[b b w w] w w [b w b w]]]

在寻找问题时,我还在(有效)模式中添加了一些检查,这可能会让您感兴趣。扩展代码如下:

(defn read-quad-tree [pattern]
    (loop   [r                   []
            [s & rs :as stack]   []
            [p & rp :as pending] (reverse pattern)]
        (cond
            (nil? pending)
                (if (or (seq stack) (not= (count r) 1))
                    {:error "Too few 'p'"}
                    {:tree (first r)})
            (= (count r) 4)
                (recur [] (conj stack (into [] (reverse r))) pending)
            (= p \p) 
                (if (empty? s)
                    {:error (format "'p' at %s lacks %d children" (count pending) (- 4 (count r)))}
                    (recur (conj r s) (into [] rs) rp))
            :else
                (recur (conj r p) stack rp))))

如果一切顺利,它将返回带有键的地图,如果模式不完整,则返回错误键。