添加到OCaml中的字符串映射

时间:2010-10-07 03:46:34

标签: ocaml

我一直试图找出我认为非常简单的任务,即在函数中向OCaml中的字符串映射添加条目。相关要素如下:

module StringMap = Map.Make (String);;
let m = StringMap.empty;;

let rec count ke = match ke with
 |[] -> []
 |hd::tl -> begin let m = StringMap.add hd 1 m; [hd] @ count tl end;;

我一直收到神秘的“语法错误”消息,但即使将代码剥离到基本没有,我还没有找到解决方案。我可以使用单个命令添加到String Map,但如果我尝试从函数内运行let m = StringMap.add hd 1 m,则无法运行。我确信这是一个简单的问题,但有人可以帮忙吗?感谢。

2 个答案:

答案 0 :(得分:15)

您的代码似乎存在一些问题。但首先,这是一个关于如何做你想做的事情的例子:

module StringMap = Map.Make (String)

let rec count m ke =
    match ke with
    | [] -> m
    | hd :: tl -> count (StringMap.add hd 1 m) tl

let m = count StringMap.empty ["foo"; "bar"; "baz"]

对原始代码的一些评论:

  • 双分号用于告诉REPL循环您希望它使用您的代码。如果不使用它们,你应该避免它们。
  • 单个分号用于分隔命令式表达式,例如完成任务。这里有一个let表达式,其语法为“let< ..> in< ..>”,因此在分号后使用分号是错误的。 (“let a = ...”没有“in”是一个顶层构造,而不是你可以在本地使用的东西。)
  • StringMap(以及ocaml中的大多数其他结构)是功能性的,而非命令性的。你不能改变你在第一行声明的'm'。您必须构建结构,最后在循环结束时返回完全构建的结构。

答案 1 :(得分:10)

您的困惑之处在于您误解了let构造的含义。它不会修改对象:它不是赋值语句(。let构造为提供名称。{{1}的语法} construct是
let 名称 let = 表达
这会导致 name 引用表达式中的 value 。在将名称绑定到它之前,计算一次该值。 name 的范围是表达式,因此您不能使用它来引用表达式之外的 value (另一方面,价值一直持续到收集垃圾为止。)

顶级in构造与表达式中的构造类似,但没有let 表达式部分。名称的范围是程序的其余部分(好像有一个in部分包含下面的所有内容,至少在你进入模块之前)。

您正在尝试修改顶级in,但这不是m的工作:您需要为此进行分配。 Ocaml有一个赋值运算符let,它分配给现有的引用。引用是可以修改的对象。这在Ocaml中不是自动的:与C,Java和Lisp等语言不同,Ocaml不使用单一语言功能为值提供名称并创建可修改的存储。 :=函数创建一个可修改的对象;您使用ref进行分配,并使用:=运算符获取其值:

!
然而,这不是很好的Ocaml风格。 Ocaml支持这种命令式,但你应该避免它,因为命令式编程比函数式编程更容易出错。功能样式是在您想要修改对象时创建新值。请注意,由let r_m = ref StringMap.empty;; (*the type of r_m is 'a StringMap.t ref*) let rec count ke = match ke with | [] -> [] | hd::tl -> r_m := StringMap.add hd 1 !r_m; [hd] @ count tl;; 模块创建的地图旨在支持此样式:Map返回与旧对象共存的新对象(即,它是持久数据结构)。切换到功能样式需要您更改add功能的界面;无论如何,这是你想要做的事情,通过将地图作为参数传递,能够在不同的地图上使用count函数。我建议您使用Sami's answer代码,以获得良好Ocaml风格的代码。