Ocaml 转换为';一张单子一套?

Ocaml 转换为';一张单子一套?,ocaml,Ocaml,OCaml真的没有从列表转换为集合的函数吗 如果是这种情况,是否可以将通用函数列表设置为?我一直在尝试创建一个多态集,但运气不好。基本问题:列表可以包含任何类型的元素。相反,集合(假设您指的是标准库的模块)依赖元素比较操作来保持平衡树。如果对t没有比较操作,则不能希望将t列表转换为集合 实际问题:标准库的Set模块已功能化:它将表示元素类型及其比较操作的模块作为输入,并生成表示集合的模块作为输出。使用列表的简单参数化多元论来完成这项工作有点运动 要做到这一点,最简单的方法是将列表函数的集合包装在

OCaml真的没有从列表转换为集合的函数吗


如果是这种情况,是否可以将通用函数
列表设置为
?我一直在尝试创建一个多态集,但运气不好。

基本问题:列表可以包含任何类型的元素。相反,集合(假设您指的是标准库的模块)依赖元素比较操作来保持平衡树。如果对
t
没有比较操作,则不能希望将
t列表
转换为集合

实际问题:标准库的
Set
模块已功能化:它将表示元素类型及其比较操作的模块作为输入,并生成表示集合的模块作为输出。使用列表的简单参数化多元论来完成这项工作有点运动

要做到这一点,最简单的方法是将列表函数的集合包装在一个函子中,以便它本身由一个比较函数参数化

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
  module SoL = SetOfList(String);;
  SoL.S.cardinal (SoL.set_of_list ["foo"; "bar"; "baz"]);; (* returns 3 *)
然后,您可以将其与字符串模块一起使用,该模块提供了一个合适的
compare
函数

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
  module SoL = SetOfList(String);;
  SoL.S.cardinal (SoL.set_of_list ["foo"; "bar"; "baz"]);; (* returns 3 *)
也可以使用非功能化集合的不同实现,例如电池和Extlib“PSet”实现()。建议使用函数化设计,因为它有更好的类型保证——不能使用不同的比较操作混合相同元素类型的集合

注:当然,如果你已经有一个给定的集合模块,从集合中实例化。Make functor,你不需要所有这些;但是你的转换函数不会是多态的。例如,假设我的代码中定义了
StringSet
模块:

module StringSet = Set.Make(String)
然后,我可以使用
stringset.add
stringset.empty来轻松编写列表的
stringset\u

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)

如果您不介意使用非常粗糙的方法,那么可以使用多态哈希表接口。元素类型为unit的哈希表只是一个集合

# let set_of_list l = 
      let res = Hashtbl.create (List.length l)
      in let () = List.iter (fun x -> Hashtbl.add res x ()) l
      in res;;
val set_of_list : 'a list -> ('a, unit) Hashtbl.t = <fun>
# let a = set_of_list [3;5;7];;
val a : (int, unit) Hashtbl.t = <abstr>
# let b = set_of_list ["yes";"no"];;
val b : (string, unit) Hashtbl.t = <abstr>
# Hashtbl.mem a 5;;
- : bool = true
# Hashtbl.mem a 6;;
- : bool = false
# Hashtbl.mem b "no";;
- : bool = true
#让列表l的集合=
让res=Hashtbl.create(List.length l)
在let()=List.iter(funx->Hashtbl.addresx())l中
在res;;
val set of_list:'a list->('a,unit)Hashtbl.t=
#设a=列表[3;5;7]的集合;;
val a:(整数,单位)Hashtbl.t=
#设b=设置列表的集合[“是”;“否”];;
val b:(字符串,单位)Hashtbl.t=
#Hashtbl.mema5;;
-:bool=true
#Hashtbl.mema6;;
-:bool=false
#Hashtbl.memb“否”;;
-:bool=true
如果您只需要测试成员资格,这可能就足够了。如果需要其他集合操作(如并集和交集),这不是一个很好的解决方案。从类型的角度来看,它肯定不是很优雅。

OCAML3.12有扩展(和),可以实例化和传递多态值的模块

在本例中,
make_set
函数为给定的比较函数返回一个
set
模块,
build_demo
函数为给定的模块和值列表构造一个集合:

let make_set (type a) compare =
  let module Ord = struct
    type t = a
    let compare = compare
  end
  in (module Set.Make (Ord) : Set.S with type elt = a)

let build_demo (type a) set_module xs =
  let module S = (val set_module : Set.S with type elt = a) in
  let set = List.fold_right S.add xs S.empty in
    Printf.printf "%b\n" (S.cardinal set = List.length xs)

let demo (type a) xs = build_demo (make_set compare) xs

let _ = begin demo ['a', 'b', 'c']; demo [1, 2, 3]; end
但这并不能完全解决问题,因为编译器不允许返回值具有依赖于模块参数的类型:

let list_to_set (type a) set_module xs =
  let module S = (val set_module : Set.S with type elt = a) in
    List.fold_right S.add xs S.empty

Error: This `let module' expression has type S.t
In this type, the locally bound module name S escapes its scope
一种可能的解决方法是返回对隐藏集值进行操作的函数集合:

let list_to_add_mem_set (type a) set_module xs =
  let module S = (val set_module : Set.S with type elt = a) in
  let set = ref (List.fold_right S.add xs S.empty) in
  let add x = set := S.add x !set in
  let mem x = S.mem x !set in
    (add, mem)

只需扩展原始类型,如中所示 对于列表模块:

module StringSet = Set.Make (* define basic type *)
  (struct
     type t = string
     let compare = Pervasives.compare
   end)
module StringSet = struct (* extend type with more operations *)
  include StringSet
  let of_list l =
    List.fold_left
      (fun s e -> StringSet.add e s)
      StringSet.empty l
end;;

使用核心库,您可以执行以下操作:

let list_to_set l = 
List.fold l ~init:(Set.empty ~comparator:Comparator.Poly.comparator)
~f:Set.add |> Set.to_list
例如:

list_to_set [4;6;3;6;3;4;3;8;2]
-> [2; 3; 4; 6; 8]
或:


感谢您的详细解释:)我想在这个特定示例中,F#中的泛型更容易使用。“我宁愿有一个正确的折叠来向集合中添加元素,如果只是为了参数顺序:
let stringset\u of_list li=list.fold\u right stringset.add li stringset.empty
谢谢:)我不保证有OCaml 3.12,因为我在大学里做编译器。如上所述,我将坚持为特定集合设置列表。