【问题标题】:Serializing sorted maps in Clojure / EDN?在 Clojure / EDN 中序列化排序的地图?
【发布时间】:2013-08-09 17:01:27
【问题描述】:

如何在 Clojure 中序列化和反序列化已排序的映射?

例如:

(sorted-map :a 1 :b 2 :c 3 :d 4 :e 5)
{:a 1, :b 2, :c 3, :d 4, :e 5}

我注意到的:

  1. 排序后的地图的显示方式与 REPL 中未排序的地图相同。这有时看起来很方便,但有时却很不方便。
  2. EDN 不支持 sorted maps
  3. Clojure 确实支持 custom tagged literals for the reader

其他资源:

【问题讨论】:

    标签: clojure sortedmap edn


    【解决方案1】:

    有两个可用答案的相同问题: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 安装自定义方法或使用自定义函数来打印您的排序地图。我可能会选择后一种解决方案——为内置类型实现内置协议/多方法通常不是一个好主意,所以即使在特定情况下看起来很合理,它也需要格外小心等;更容易使用自己的功能。

    更新:

    演示如何干净地重用IPersistentMapprint-method impl,正如在对大卫回答的评论中所承诺的那样:

    (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}
    

    【讨论】:

    • 注意。在 Clojure 1.5 中引入了 clojure.edn 命名空间。
    【解决方案2】:

    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) 会丢失排序并因此搞砸打印,但它在我的测试中有效。我不知道为什么。

    【讨论】:

    • 测试通过是因为它们很小,所以基本上是“偶然”(Clojure 的实现细节)。 (测试的大小很重要,因为对于小映射(into {} ...) 创建了数组映射,它保留了插入顺序。然后当它们变得足够大时,这些映射将被转换为哈希映射。)clojure.lang.PersistentTreeMap/create 是一个命名 Java 方法的符号;这些在 Clojure 中并不是一流的。您可以将它包装在一个函数中——#(clojure.lang.PersistentTreeMap/create %)——这将比用作一个值,但实际上你当前的解决方案对我来说看起来更好。
    • 如果你想通过调整重用print-method 的现有实现为clojure.lang.IPersistentMap,你可以(1)用(get (methods print-method) clojure.lang.IPersistentMap) 提取它(它只是一个函数),(2)存储它在方便的地方,(3)从您自己的print-method 实现中调用它。我将编辑我的答案以进行演示。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    相关资源
    最近更新 更多