添加到Ocaml中的一个集合

时间:2017-07-28 11:02:09

标签: ocaml

我在OCaml中有这个基本问题,现在已经困扰了我很久了。我有模式匹配,比较用户的输入并打印出连接的字符串。这工作正常,但我现在需要将此连接字符串添加到模式匹配中的集合。每次我尝试这样做时都会收到错误:

This expression has type SS.t = Set.Make(String).t
   but an expression was expected of type string

我对OCaml很陌生,这已经花了这么多天,而我却无法弄明白。模式匹配的代码如下:

type t = Zero | Pproc of string | Procdef of t * t

module SS = Set.Make(String)
let set2= SS.empty

let concattoset s =
    List.fold_right SS.add [s] set2

let rec poc p = match p with
  | Zero -> concattoset "0"
  | Pproc x -> concattoset x
  | Procdef (p1, p2) -> concattoset((poc p1)^"("^(poc p2)^")")

我遇到的错误是:

Procdef (p1, p2) -> concattoset((poc p1)^"("^(poc p2)^")");;
                                ^^^^^^^^
Error: This expression has type SS.t = Set.Make(String).t
       but an expression was expected of type string

2 个答案:

答案 0 :(得分:3)

我编辑了您的问题,但一旦经过同行评审,它就可以使用了。然而,让我们回答你的问题(顺便说一下,不要说“谢谢!”并尝试举例说明有助于重现你的错误。)

因此,您的代码如下所示:

type t = Zero | Pproc of string | Procdef of t * t

module SS = Set.Make(String)
let set2= SS.empty

let concattoset s =
    List.fold_right SS.add [s] set2

let rec poc p = match p with
  | Zero -> concattoset "0"
  | Pproc x -> concattoset x
  | Procdef (p1, p2) -> concattoset((poc p1)^"("^(poc p2)^")")

你的错误是:

Procdef (p1, p2) -> concattoset((poc p1)^"("^(poc p2)^")");;
                                ^^^^^^^^
Error: This expression has type SS.t = Set.Make(String).t
       but an expression was expected of type string

首先,一个集合是不可变的,因此SS.add将返回一个新集合,而不是unit

根据此,concattoset的类型为SS.elt -> SS.t,则poc的返回类型与concattoset "0")的类型相同。

那是因为当编译器尝试键入一个匹配模式的函数时,它会根据模式匹配的每个案例返回的内容推断出类型:

let rec poc p = match p with
   | Zero -> concattoset "0"
   | ...

此处p的类型为t,因为它与Zero匹配且concattoset的类型为string -> SS.t,因此concattoset "0"属于SS.t类型poc然后t (*the type of p*) -> SS.t (the type returned by concattoset "0"的类型为concattoset ((poc p1) ^ poc p2))

当您致电poc p1时,concattoset : string -> SS.t) but has the type应该是一个字符串((the return type of SS.t type t = Zero | Pproc of string | Procdef of t * t module SS = Set.Make(String) let concattoset ?s1 ?sep1 ?sep2 s2 set = let str = Printf.sprintf "%s%s%s%s" (match s1 with None -> "" | Some s -> s) (match sep1 with None -> "" | Some s -> s) s2 (match sep2 with None -> "" | Some s -> s) in SS.add str set;; let rec poc p set = match p with | Zero -> concattoset "0" set | Pproc x -> concattoset x set | Procdef (p1, p2) -> concattoset ~s1:(poc p1) ~sep1:"(" ~sep2:")" (poc p2) set poc p1`),这就是编译器告诉您的内容。< / p>

您可以编写的代码(丑陋)是:

concattoset

但这样做有点尴尬,如果你的类型有多个构造函数,很快就会很难写出一个好的let rec pp fmt = function | Zero -> Format.fprintf fmt "0" | Pproc x -> Format.fprintf fmt "%s" x | Procdef (p1, p2) -> Format.fprintf fmt "%a(%a)" pp p1 pp p2 | Timer(t,chan, var, p1, p2) -> Format.fprintf fmt "timer%s(%s(%s).%a,%a)" t chan var pp p1 pp p2;; let concattoset s set = SS.add s set;; let poc p set = concattoset (Format.asprintf "%a" pp p) set;;

我要做的是以下(为更复杂的构造函数添加Timer):

pp

因此,concattoset采用格式化程序(可以看作输出缓冲区),在构造函数中打印必要的递归调用。 SS.add就像poc一样简单,t采用pp类型的参数和一个集合,并使用格式化程序作为字符串调用concattoset并给出此%a的新字符串,这正是您想要的。

关于pp

Format.asprintf "%a" pp p期待2个参数可能看起来有点奇怪,但我打电话时只给出一个,例如pp。好吧,实际上,我没有将p应用于Format.asprintf "%a" (pp p),在这里,我正在给打印机提供2个参数,如文档中所述:

  

a:用户定义的打印机。拿两个参数并应用第一个参数   outchan(当前输出通道)和第二个参数。该   因此,第一个参数必须具有类型out_channel - &gt; 'b - &gt;单位和   第二个'b。函数产生的输出插入到   当前点的fprintf输出。

这就是编译器不接受编写^(AY)0*([1-9].*)的原因。

答案 1 :(得分:1)

我无法重现您的错误。当我尝试使用类型的简化版本的类似示例时,没有错误。

# let set2 = SS.empty;;
val set2 : SS.t = <abstr>
# let concattoset s = List.fold_right SS.add [s] set2;;
val concattoset : SS.elt -> SS.t = <fun>
# let proc p = match p with
| None -> concattoset "0"
| Some s -> concattoset s;;
val proc : SS.elt option -> SS.t = <fun>
# SS.elements (proc (Some "def"));;
- : SS.elt list = ["def"]

然而,在我看来,你希望能够将一个集合视为一个可变容器。 OCaml中的一个集是不可变的。如果你想构建更大更大的集合,你需要将一个输入集传递给你的函数,并让函数添加到该集合(而不是空集),并返回结果。

这是一个会话,可以说明集合是不可变的意义:

# module SS = Set.Make(String);;
module SS : sig ... end
# let s = SS.empty;;
val s : SS.t = <abstr>
# SS.elements s;;
- : SS.elt list = []
# SS.add "abc" s;;
- : SS.t = <abstr>
# SS.elements s;;
- : SS.elt list = []

表达式SS.add "abc" s返回包含字符串"abc"的新集合。它不会更改s,这是不可变的。

在OCaml中,当您谈到将字符串添加到集合时,这意味着您正在创建包含字符串的 new 集合。前一组(不可变)不会改变。

这是一个函数,它将两个给定的字符串添加到一个集合中,并返回新的集合:

let add2 set s1 s2 =
    SS.add s1 (SS.add s2 set)

set参数是输入集。该函数的结果是一个新的,更大的集合。输入集在过程中不会更改(它是不可变的)。