Jane Street Core有一个非常方便的'a Core.Set.Poly.t
,这是一个基于多态Pervasives.compare
的多态集。
例如,此集具有映射map
的{{1}}函数。为了进行比较,标准OCaml集是基于仿函数的,并生成一个单态集,'a -> 'b
函数映射map
。
是否有方便的方式来制作或模仿这样的" Poly"仅使用标准库设置,而无需从头开始自己的实现?
答案 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模块将为每个操作复制(并且遍历)您的设置元素。