基本问题:列表可以包含任何类型的元素。集合(假设你的意思是Set http://caml.inria.fr/pub/docs/manual-ocaml/libref/Set.html标准库的模块),相反,依靠元素比较操作来保持平衡树。你不能指望将一个t list
到一个集合,如果你没有比较操作t
.
实际问题:Set
标准库的模块是函式化的:它作为输入module表示您的元素类型及其比较操作,并生成输出 amodule代表集合。使用列表的简单参数多态性来实现这一点有点运动。
为此,最简单的方法是将 set_of_list 函数包装在函子中,以便它本身由比较函数参数化。
module SetOfList (E : Set.OrderedType) = struct
module S = Set.Make(E)
let set_of_list li =
List.fold_left (fun set elem -> S.add elem set) S.empty li
end
然后,您可以使用 String 模块,它提供了合适的compare
功能。
module SoL = SetOfList(String);;
SoL.S.cardinal (SoL.set_of_list ["foo"; "bar"; "baz"]);; (* returns 3 *)
还可以使用非函子化集的不同实现,例如电池和 Extlib“PSet”实现(文档 http://ocaml-batteries-team.github.com/batteries-included/hdoc/BatSet.html#6_Polymorphicsets)。建议使用函子化设计,因为它具有更好的类型保证——您不能使用不同的比较操作来混合相同元素类型的集合。
注意:当然,如果你already有一个给定的 set 模块,从 Set.Make 函子实例化,你不需要所有这些;但你的转换函数不会是多态的。例如假设我有StringSet
我的代码中定义的模块:
module StringSet = Set.Make(String)
然后我可以写stringset_of_list
轻松地,使用StringSet.add
and StringSet.empty
:
let stringset_of_list li =
List.fold_left (fun set elem -> StringSet.add elem set) StringSet.empty li
如果您不熟悉折叠,这里有一个直接的、非尾递归的递归版本:
let rec stringset_of_list = function
| [] -> StringSet.empty
| hd::tl -> StringSet.add hd (stringset_of_list tl)