2010-07-08 28 views
43

新手問題,但我真的不明白爲什麼有這麼多的操作構建地圖clojure。clojure爲什麼有這麼多的地圖構建函數?

你有conjassocmerge,但他們似乎或多或少做同樣的事情?

(assoc {:a 1 :b 2} :c 3) 
(conj {:a 1 :b 2} {:c 3}) 
(merge {:a 1 :b 2} {:c 3}) 

真的有什麼區別,爲什麼所有這些方法都需要它們做更多或更少的相同的事情?

+6

還有'(進入{:1:B 2} {:C 3})' – VitoshKa 2014-03-22 03:50:53

回答

47

assocconj表現非常不同的其他數據結構:

user=> (assoc [1 2 3 4] 1 5) 
[1 5 3 4] 
user=> (conj [1 2 3 4] 1 5) 
[1 2 3 4 1 5] 

如果你正在編寫一個可處理多種類型的集合的功能,那麼你的選擇將有很大的不同。

merge視爲僅限地圖功能(其類似於其他集合的conj)。

我的看法:

  • assoc命令 - 使用當你是'改變現有的鍵/值對
  • 連詞 - 使用,當你 '添加' 新鍵/值對
  • 合併 - 在組合兩張或更多地圖時使用
+4

'merge'需要任意數量的地圖併合並它們,而不僅僅是兩個。 – ponzao 2010-07-08 14:16:13

+1

好點。改變了我的措辭。 – dbyrne 2010-07-08 14:18:23

+0

沒有問題,+1一個很好的答案。 – ponzao 2010-07-08 14:20:09

6

由於地圖在Clojure中是一個無處不在的數據結構,因此擁有多個工具來處理它們是很有意義的。在稍有不同的情況下,各種不同的功能在語法上都是方便的。

我個人拿上你提到的具體功能:

  • 我用assoc命令爲單個值添加到給定的鍵和值
  • 我用合併兩個地圖相結合的地圖或一次添加多個新條目
  • 我一般不會使用conj與地圖,因爲我在心理上將它與列表聯繫起來
+2

我認爲慣用的方法將是使用的任何功能,其中所加入的新項目的格式匹配到地圖最初變得可用;如果它是地圖,使用'merge',如果作爲一組鍵和值不集合在一個集合中,使用'assoc'等。我真正想要指出的是,'conj'是* universal * Clojure數據結構構建函數 - 你不能真正將它保存在列表抽屜中。 – 2010-07-08 17:46:45

+0

@Michal也許你是對的 - 但由於某種原因,我對函數不信任,它們在不同的輸入類型上進行語義上非常不同的事情,而沒有警告:-) – mikera 2010-07-08 19:31:19

+4

我會爭辯說,'conj' **在語義上等同於操作在不同的輸入類型。 ;) – dbyrne 2010-07-08 19:59:39

21

實際上,這些函數在與地圖一起使用時表現得非常不同。

  1. conj

    首先,從問題文本(conj {:a 1 :b 2} :c 3)例子並不在所有的工作(既不與1.1也不與1.2; IllegalArgumentException拋出)。在地圖上只有少數幾種類型,即兩元素矢量,clojure.lang.MapEntry(它們基本上相當於二元矢量)和地圖。

    請注意,地圖的seq包含一堆MapEntry s。因此你可以做例如

    (into a-map (filter a-predicate another-map)) 
    

    (注意:into使用conj - 或conj!,如果可能的話 - 內部)。 mergeassoc都不允許你這樣做。

  2. merge

    這幾乎完全等同於conj,但它取代了nil論點{} - 空哈希地圖 - 因此會返回地圖時,第一個「地圖」鏈發生成爲nil

    (apply conj [nil {:a 1} {:b 2}]) 
    ; => ({:b 2} {:a 1}) ; clojure.lang.PersistentList 
    (apply merge [nil {:a 1} {:b 2}]) 
    ; => {:a 1 :b 2} ; clojure.lang.PersistentArrayMap 
    

    注意沒有什麼(除了文檔字符串...),從使用merge與其他集合類型停止程序員。如果有人那樣做,就會出現怪異現象;不建議。

  3. assoc

    再次,從試題內容的例子 - (assoc {:a 1 :b 2} {:c 3}) - 將無法正常工作;相反,它會拋出IllegalArgumentExceptionassoc需要一個映射參數,然後是偶數個參數 - 奇數位置(假設地圖位於位置0)是鍵,偶數位置是值。我發現我的assoc東西地圖更經常比我conj,雖然當我conjassoc會感到麻煩。 ;-)

  4. merge-with

    爲了完整起見,這是處理地圖最終的基本功能。我覺得它非常有用。它按照文檔字符串的說明工作。這裏有一個例子:

    (merge-with + {:a 1} {:a 3} {:a 5}) 
    ; => {:a 9} 
    

    注意,如果地圖中包含一個「新」鍵,未在任何地圖,它的左側的發生,合併功能將不會被調用。這偶爾會令人沮喪,但在1.2中,巧妙的reify可以提供非nil「默認值」的地圖。

+0

爲了說明最後一點'merge-with',我準備了以下Gist:http://gist.github.com/468332 – 2010-07-08 17:28:22

+0

感謝您的解釋。正如你看到的,我有一個打字錯誤,並轉換了conj和assoc之間的第二個參數。 – grm 2010-07-08 22:02:16

相關問題