函数Map.make中合并的OCaml语义?

时间:2010-09-20 12:48:21

标签: ocaml

我正在编写一个OCaml函数,我需要合并两个映射。我无法弄清楚仿函数merge(在OCaml的3.12.0版本中找到)提供的Map.Make函数的语义。有人可以向我提供比OCaml手册更详细的解释吗?一个例子可能足以让我弄清楚。

此外,我需要合并的两个地图有一些有趣的属性:键具有相同的类型(实际上是int),并且它们的域是不相交的。是否有比合并例程更有效的方法?

3 个答案:

答案 0 :(得分: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

答案 1 :(得分: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

它基本上遍历map中的所有元素,并调用一个带有键,值和类型'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(n log(m))。

答案 2 :(得分: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

有没有更有效的方法来做到这一点?

- 大卫。