如何在 Clojure 中序列化和反序列化已排序的映射?
例如:
(sorted-map :a 1 :b 2 :c 3 :d 4 :e 5)
{:a 1, :b 2, :c 3, :d 4, :e 5}
我注意到了什么:
- 排序后的地图以与 REPL 中未排序的地图相同的方式显示。这有时看起来很方便,但有时却不方便。
- EDN不支持已排序的地图。
- Clojure 确实支持读者的自定义标记文字。
其他资源:
如何在 Clojure 中序列化和反序列化已排序的映射?
例如:
(sorted-map :a 1 :b 2 :c 3 :d 4 :e 5)
{:a 1, :b 2, :c 3, :d 4, :e 5}
我注意到了什么:
其他资源:
同样的问题有两个可用的答案:Saving+reading sorted maps to a file in Clojure。
第三个答案是设置自定义阅读器文字。您可以将排序后的地图打印为
;; non-namespaced tags are meant to be reserved
#my.ns/sorted-map {:foo 1 :bar 2}
然后在读取时使用适当的数据函数(从哈希映射转换为排序映射)。您可以选择是否要处理自定义比较器(这是一个一般无法解决的问题,但当然可以选择处理特殊情况)。
clojure.edn/read
接受opts
可能包含:reader
键的可选映射;然后将该键的值作为一个映射,指定哪些数据阅读器用于哪些标签。详情请参阅(doc clojure.edn/read)
。
至于打印,您可以安装自定义方法print-method
或使用自定义函数来打印排序的地图。我可能会选择后一种解决方案——为内置类型实现内置协议/多方法通常不是一个好主意,所以即使在特定情况下看起来很合理,它也需要格外小心等;更容易使用自己的功能。
更新:
正如对大卫回答的评论中所承诺的那样,演示如何干净地IPersistentMap
重用impl :print-method
(def ^:private ipm-print-method
(get (methods print-method) clojure.lang.IPersistentMap))
(defmethod print-method clojure.lang.PersistentTreeMap
[o ^java.io.Writer w]
(.write w "#sorted/map ")
(ipm-print-method o w))
有了这个:
user=> (sorted-map :foo 1 :bar 2)
#sorted/map {:bar 2, :foo 1}
在data_readers.clj
:
{sorted/map my-app.core/create-sorted-map}
注意:我希望这会起作用,但它没有(不知道为什么):
{sorted/map clojure.lang.PersistentTreeMap/create}
现在,在my-app.core
:
(defn create-sorted-map
[x]
(clojure.lang.PersistentTreeMap/create x))
(defmethod print-method clojure.lang.PersistentTreeMap
[o ^java.io.Writer w]
(.write w "#sorted/map ")
(print-method (into {} o) w))
作为替代方案 - 较低级别,您可以使用:
(defn create-sorted-map [x] (into (sorted-map) x))
测试:
(deftest reader-literal-test
(testing "#sorted/map"
(is (= (sorted-map :v 4 :w 5 :x 6 :y 7 :z 8)
#sorted/map {:v 4 :w 5 :x 6 :y 7 :z 8}))))
(deftest str-test
(testing "str"
(is (= "#sorted/map {:v 4, :w 5, :x 6, :y 7, :z 8}"
(str (sorted-map :v 4 :w 5 :x 6 :y 7 :z 8))))))
其中大部分改编自我在上面找到的资源。
注意:我很惊讶print-method
上面的作品。在我看来,这(into {} o)
会丢失排序并因此搞砸打印,但它在我的测试中有效。我不知道为什么。