2010-09-20 39 views
6

我正在寫一個OCaml函數,我需要合併兩個地圖。我一直無法弄清函數Map.Make(在OCaml的3.12.0版本中找到)提供的merge函數的語義。有人能給我提供比OCaml手冊更詳細的解釋嗎?一個例子可能足以讓我弄清楚。函子Map.make中的合併OCaml語義?

此外,我需要合併的兩個地圖有一些有趣的屬性:鍵具有相同類型(實際上爲int),並且它們的域是不相交的。會有比合並例程更有效的方法嗎?

+3

當鍵的類型爲'int'並且您有興趣合併(不相交或不相交)的地圖時,值得檢查表示爲帕特里夏樹的地圖是否適合您的需要。這是一個實現:http://www.lri.fr/~filliatr/ftp/ocaml/ds/ptmap.ml.html – 2010-09-20 19:01:53

+0

順便說一句,如果其中一個回答解決了您的問題,您應該將其標記爲已接受。 – 2010-09-23 14:19:04

回答

10

merge需要一個函數和兩個映射。對於每個地圖中的每個鍵,都會調用該函數。如果鍵只出現在其中一個映射中,另一個鍵的值將以None傳遞(這就是參數爲選項的原因)。如果函數返回Some x,則新映射的值將爲x。否則,密鑰將不存在。

實施例:

let map1 = add 1 2 (add 2 3 empty);; 
let map2 = add 2 5 (add 3 4 empty);; 
let map3 = merge (fun k xo yo -> match xo,yo with 
    | Some x, Some y -> Some (x+y) 
    | _ -> None 
) map1 map2;; 

現在MAP3包含映射2 -> 8

如果將其更改爲:

let map3 = merge (fun k xo yo -> match xo,yo with 
    | Some x, Some y -> Some (x+y) 
    | None, yo -> yo 
    | xo, None -> xo 
) map1 map2;; 

它將包含映射1 -> 22 -> 83 -> 4

+0

晶瑩剔透,謝謝。 – David 2010-09-20 14:27:30

+0

只是澄清一點,我認爲在merge的定義中會很有用:'merge'將通過m1或m2中的所有鍵遍歷f,但只會保留返回值不是None的那些鍵。 – anol 2012-11-14 11:19:54

1

基於第一個答案,並考慮額外的問題(合併不相交的域的情況下圖),然後,我會提出下面的一般合併程序:

let merge_disjoint m1 m2 = 
    IntMap.merge 
    (fun k x0 y0 -> 
     match x0, y0 with 
     None, None -> None 
     | None, Some v | Some v, None -> Some v 
     | _, _ -> invalid_arg "merge_disjoint: maps are not disjoint") 
    m1 m2 

會不會有一些更有效的方法去做這個?

- David。

+0

這將是一個很好的'Map.union'功能。對我來說,合併地圖的想法允許一些功能不僅適用於相同的鍵,而且即使在一個地圖中也適用。例如,也許有人會想'Map.merge(fun _ x y - >(x,y))'' – Thelema 2011-12-25 18:56:15

2

由於您的地圖是不相交的,那麼你可以只通過小地圖環和插入更大:

let disjoint_merge m1 m2 = 
    if (IntMap.cardinal m1) < (IntMap.cardinal m2) then 
    IntMap.fold IntMap.add m1 m2 
    else 
    IntMap.fold IntMap.add m2 m1 

倘若您使用的是舊版本的OCaml的是不包括cardinal函數,您可以選擇一個地圖來始終迭代。至於上面的代碼做什麼,是它使用:

val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b 

這基本上遍歷所有元素的地圖,並調用一個函數,它的關鍵,價值和'b類型的其他一些變量,返回的東西該類型('b)。在我們的例子中,我們正在傳遞函數IntMap.add,而其他變量是我們的第二張地圖。因此,它遍歷所有元素並將它們添加到其他地圖。

編輯:你最好只是在做:

let disjoint_merge m1 m2 = 
    IntMap.fold IntMap.add m1 m2 

EDIT2:甚至更好:

let disjoint_merge = IntMap.fold IntMap.add;; 

我只是看着的cardinal實施,它走的樹,並返回計數。所以通過檢查大小,你正在做O(n + m + min(n,m)log(max(n,m)))而不是O(nlog(m))。