2011-12-02 53 views
3

我在地圖中有原子,這可能是也可能不是一個好主意,但重點是我需要去掉原子,以便可以對地圖執行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)) 

這似乎工作,但a)是好的,b)有沒有更好的方法?

回答

11

我認爲你的代碼可以正常工作。

一些一般性的反饋:在地圖

  • 原子是有點反模式 - 通常你試圖把地圖中的原子。這個想法是儘可能地保持你的數據結構不變,並且讓你的引用保持整個數據結構在一個相當精細的級別。我強烈建議重新考慮這個設計 - 從長遠來看,這可能會傷害到你(例如,大多數Clojure庫函數會假定不可變的地圖)。
  • 你並不是在解密密鑰。也許你永遠不會在鍵中使用原子,在這種情況下這很好,但認爲值得指出。
  • 通常在Clojure中,右括號不會得到自己的行,並且會在前一行的結尾處出現。起初這可能會讓人覺得奇怪,但你應該採用Lisp風格。
  • 這個函數的執行效果可能不是那麼好,因爲它會一塊一塊地重建整個地圖結構,即使沒有任何變化。減少可以用來改善這一點,只需要在需要時改變地圖。
  • 我認爲最好是測試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)) 
+1

給予好評的「地圖中的原子是有點反模式的」 – Bill

+0

謝謝你們全面的回答!我會從中學到很多東西。我對地圖中的原子根本不滿意,所以現在我會找到重新設計東西的方法。恐怕我不能同意關閉括號,但我必須把它們放在一個換行符上,以符合它們同意的功能,否則我會感到困惑。另外,在它們之間添加和刪除線條更容易。 – Hendekagon

+0

我不知道你可以使用像這樣的循環 - 沒有(循環 - 所以可以重複使用任何綁定形式(是像let,loop,defn,fn等術語?) – Hendekagon