序列化Zipper?

3
repl> (-> root zip/down zip/right)
[{:answer-keys [5 6], :id 3} {:l [{:id 2, :answer-keys []}], :pnodes [{:answer-keys [2 3 4], :id 1}], :ppath nil, :r ({:answer-keys [7], :id 4})}]

当我在repl上打印拉链时,我看到了这些数据。我想这可能是我需要序列化拉链的所有数据?提供的数据是否可以反序列化成拉链?

我正在寻找像下面想象的zip/serialize和zip/deserialize函数。

(def s (zip/serialize (-> root zip/down zip/right))) ;; s is a string
(def d (zip/deserialize s)) ;; d is a zipper location
;;And I can go on using the deserialized zipper d without any difficulty.

有人知道如何做到这一点吗?

(涉及IT技术)
2个回答

4

拉链的魔力在于它们是表示产生树形结构任意修改版本所需的所有内容的数据结构。由于它们是适当的值且不需要任何状态,所以拉链可以很好地打印和读取。

您可以使用pr-str序列化它,并使用read反序列化它。

创建一个拉链:

user> (zip/vector-zip [[1 [2]][3][4]])
[[[1 [2]] [3] [4]] nil]
user> (def s (zip/vector-zip [[1 [2]][3][4]]))
#'user/s
user> s
[[[1 [2]] [3] [4]] nil]

将其序列化为字符串:

user> (def serialized-s (pr-str (zip/next s)))
#'user/serialized-s
user> serialized-s
"[[1 [2]] {:l [], :pnodes [[[1 [2]] [3] [4]]], :ppath nil, :r ([3] [4])}]"

请将其读回:

user> (def deserialized-s (read-string "[[1 [2]] {:l [], :pnodes [[[1 [2]] [3] [4]]], :ppath nil, :r ([3] [4])}]"))
#'user/deserialized-s

处理结果:

user> (zip/root deserialized-s)
[[1 [2]] [3] [4]]

哇,这太酷了。我一直以为它会在整个路径上创建一个闭包。哈哈。那么我唯一剩下的问题是,为什么print-str序列化表达式,而read-string只从字符串中反序列化第一个项?它似乎有点奇怪,它们不完全“匹配”。 - Stephen Cagle
1
print 函数族是为人类阅读而设计的,而不是用于序列化。例如,字符串和符号打印相同,因此会丢失信息。相反,请使用 pr-str,它会以计算机友好的格式进行打印(并且与 read-string 函数几乎相反)。 - amalloy
@StephenCagle 如果你有另一个与此问题基本无关的问题,我建议你将其作为一个问题提出,而不是在这里的答案下发表评论。但简而言之,没有任何不一致性:print-string 将一个值打印到字符串中,而 read-string 从字符串中读取一个值。 - amalloy
@amalloy,已修复 :) 幸运的是,在这种情况下输出是相同的。 - Arthur Ulfeldt
就我理解的zipper,它们实际上代表了子项的状态以及正确类型的导航操作。 - Bastl

3

对Arthur的回答进行补充:

你可能还需要序列化clojure.zip在其拉链上放置的元数据,因为那似乎是它跟踪branch?/children/make-node函数的地方。因此,可以尝试类似以下的代码:

(defn serialize [zipper]
  (binding [*print-meta* true]
    (pr-str zipper)))

(def deserialize read-string)

这并不是特别有用,因为所有的元数据值都是函数,通常很难阅读。 - Brandon Bloom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接