我一直试图找出我认为非常简单的任务,即在函数中向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
,则无法运行。我确信这是一个简单的问题,但有人可以帮忙吗?感谢。
答案 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"]
对原始代码的一些评论:
答案 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风格的代码。