多态集与基于仿函数的Set的多态比较

时间:2017-03-20 18:50:41

标签: set ocaml

Jane Street Core有一个非常方便的'a Core.Set.Poly.t,这是一个基于多态Pervasives.compare的多态集。

例如,此集具有映射map的{​​{1}}函数。为了进行比较,标准OCaml集是基于仿函数的,并生成一个单态集,'a -> 'b函数映射map

是否有方便的方式来制作或模仿这样的" Poly"仅使用标准库设置,而无需从头开始自己的实现?

2 个答案:

答案 0 :(得分:3)

stdlib Set不能是多态的,因为它的类型构造函数没有任何参数。但是,您可以使用仿函数将map从任意集合表示为任意集合:

module MapSet (S1:Set.S) (S2:Set.S) = struct
  let map f a = S1.fold (fun elt set -> S2.add (f elt) set) a S2.empty
end

用法:

module IntSet = Set.Make (struct type t = int let compare = compare end)
module StringSet = Set.Make (String)

module MapSI = MapSet (StringSet) (IntSet)

# IntSet.elements (MapSI.map String.length (StringSet.of_list ["a"; "zonk"; "blart"]));;
- : IntSet.elt list = [1; 4; 5]

正如你所看到的,它很简单但又冗长而且“有名”。

另一种选择是使用('a, unit) Hashtbl作为可变集合,它将在引擎盖下使用多态相等(具有所有常见的缺点)。您可以轻松实现自己的map操作:

let map_keys f tbl =
  let new_tbl = Hashtbl.create (Hashtbl.length tbl) in
  Hashtbl.iter (fun k v -> Hashtbl.replace new_tbl (f k) v) tbl;
  new_tbl

请注意Hashtbl不提供Set所做的最坏情况保证,或有效的联合/差异/交叉操作。

答案 1 :(得分:0)

以下是使用标准库中的Marshal和Set的概念验证。争论的焦点是它不是从头开始构建的,因为它只是Set上的一个薄层。

module PSet = Set.Make(struct type t = bytes let compare = compare end)

module type POLYSET = sig
    type 'a t
    val empty : 'a t
    val mem : 'a -> 'a t -> bool
    val add : 'a -> 'a t -> 'a t
    val fold : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'b
    val map : ('a -> 'b) -> 'a t -> 'b t
end

module PolySet : POLYSET = struct
    type 'a t = PSet.t
    let wrap x = Marshal.to_bytes x []
    let unwrap b = Marshal.from_bytes b 0
    let empty = PSet.empty
    let mem elt set =
        PSet.mem (wrap elt) set
    let add elt set =
        PSet.add (wrap elt) set
    let fold f set init =
        PSet.fold (fun bytes acc -> f (unwrap bytes) acc) set init
    let map f set =
        fold (fun elt bset -> add (f elt) bset) set empty
end

它对我有用:

# module P = Sm.PolySet;;
module P = Sm.PolySet
# let x = P.add 14 (P.add 88 P.empty);;
val x : int P.t = <abstr>
# P.mem 14 x;;
- : bool = true
# P.mem 15 x;;
- : bool = false
# P.mem "abc" x;;
Error: This expression has type int P.t = int Sm.PolySet.t
       but an expression was expected of type
         string P.t = string Sm.PolySet.t
       Type int is not compatible with type string
# P.fold (fun x l -> x :: l) x [];;
- : int list = [88; 14]
# let y = P.map float_of_int x;;
val y : float P.t = <abstr>
# P.fold (fun x l -> x :: l) y [];;
- : float list = [88.; 14.]

<强>更新

公平地说,我应该指出,这种解决方案的便利是有代价的。 Marshal模块将为每个操作复制(并且遍历)您的设置元素。