Сериализация отсортированных карт в Clojure / EDN?

Как я могу сериализовать и десериализовать отсортированную карту в 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 не имеет поддержки отсортированных карт.
  3. Clojure поддерживает собственные литералы с тегами для читателя.

Дополнительные ресурсы:

2 ответа

Решение

Тот же вопрос с двумя полезными ответами: Сохранение + чтение отсортированных карт в файл в 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"s 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) потерял бы порядок и таким образом запутал бы печать, но это работает в моем тестировании. Я не знаю почему.

Другие вопросы по тегам