Деатомизация карты - PullRequest
       13

Деатомизация карты

3 голосов
/ 02 декабря 2011

У меня есть атомы на картах, что может быть хорошей идеей, а может и не быть, но дело в том, что мне нужно разыменовать атомы, чтобы я мог json-str карты, а json-str не может обрабатывать атомы, поэтому написал это:

(defn deatomize- [m]
 (cond
   (instance? clojure.lang.Atom m) #(deatomize- @m)
   (map? m) (zipmap (keys m) (map #(trampoline deatomize- %) (vals m)))
   :else m
 )
)

(defn deatomize [m] (trampoline deatomize- m))

что, кажется, работает, но а) это хорошо, б) есть ли лучший способ?

1 Ответ

11 голосов
/ 02 декабря 2011

Я думаю, ваш код будет работать нормально.

Некоторые общие отзывы:

  • атомы в картах - это немного анти-паттерн - обычно вы пытаетесь поместить карты в атомы. Идея состоит в том, чтобы сохранить ваши структуры данных настолько неизменными, насколько это возможно, и чтобы ваши ссылки содержали целые структуры данных на достаточно детальном уровне. Я настоятельно рекомендую переосмыслить этот дизайн - он, вероятно, навредит вам в долгосрочной перспективе (например, большинство функций библиотеки Clojure предполагают неизменные карты)
  • Вы не деатомизируете ключи. возможно, вы никогда не используете атомы в ключах, и в этом случае это нормально, но подумал, что на это стоит обратить внимание.
  • обычно в Clojure закрывающие скобки не получают свою собственную строку и идут в конце предыдущей строки. поначалу это может показаться странным, но это хороший стиль Лиспа, который вы, вероятно, должны принять.
  • производительность этой функции может быть не такой уж высокой, потому что она перестраивает всю структуру карты по частям, даже если ничего не меняется. Уменьшение можно использовать для улучшения этого, изменяя карту только тогда, когда вам нужно.
  • Я думаю, что лучше проверять clojure.lang.IDeref, а не clojure.lang.Atom. Благодаря этому ваш код будет обрабатывать и другие ссылочные типы, если это необходимо.
  • батут будет увеличивать накладные расходы и нужен только в (предположительно, редком?) Случае, когда у вас действительно глубокая вложенность в структуру данных, которая может вызвать переполнение стека. Если вам нужна как безопасная обработка стека, так и хорошая производительность, вы можете рассмотреть рекурсивную реализацию и возврат к батутной версии в случае StackOverflowException.
  • если вы найдете атом, функция на самом деле рекурсивна, поэтому вы можете использовать recur. это уменьшит необходимость использовать батут!

Вот альтернатива для рассмотрения:

(defn deatomize [m] 
  (cond
    (instance? clojure.lang.IDeref m) 
      (recur @m)
    (map? m) 
      (reduce 
        (fn [current-map [k v]] 
          (let [nv (deatomise v)] 
            (if (= v nv) current-map (assoc current-map k nv)))) 
        m 
        m)
    :else m))
...