我正在编写一个OCaml函数,我需要合并两个映射。我无法弄清楚仿函数merge
(在OCaml的3.12.0版本中找到)提供的Map.Make
函数的语义。有人可以向我提供比OCaml手册更详细的解释吗?一个例子可能足以让我弄清楚。
此外,我需要合并的两个地图有一些有趣的属性:键具有相同的类型(实际上是int
),并且它们的域是不相交的。是否有比合并例程更有效的方法?
答案 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 -> 2
,2 -> 8
和3 -> 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
有没有更有效的方法来做到这一点?
- 大卫。