添加到OCaml中的字符串映射

dua*_*mat 8 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;;
Run Code Online (Sandbox Code Playgroud)

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

Sam*_*ami 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"]
Run Code Online (Sandbox Code Playgroud)

对原始代码的一些评论:

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

  • 很好的答案(虽然我认为它值得[更多解释](http://stackoverflow.com/questions/3878527/adding-to-a-string-map-in-ocaml/3879553#3879553)),除了我不会建议不要使用双分号.他们不需要,但他们也没有伤害. (2认同)

Gil*_*il' 10

您的困惑之处在于您误解了let构造的含义.它不修改一个对象:它不是一个赋值语句(该let结构提供了一个名字到一个的语法,let结构是
    let 名义 = in 表达式
这将导致名称来指代表达式的值计算一次,结合之前.名字吧.范围名称表达,所以你不能用它来指代以外表达式(值,而另一方面,住,直到收集的垃圾).

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

你试图修改顶级m,但这不是let工作:你需要一个任务.Ocaml有一个赋值运算符,:=它分配给现有引用.引用是可以修改的对象.这在Ocaml中不是自动的:与C,Java和Lisp等语言不同,Ocaml不使用单一语言功能为值赋值并创建可修改的存储.该ref函数创建一个可修改的对象; 你赋予它:=并使用!运算符来获取它的值:

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;;
Run Code Online (Sandbox Code Playgroud)

然而,这不是很好的Ocaml风格.Ocaml支持这种命令式,但你应该避免它,因为命令式编程比函数式编程更容易出错.功能样式是在您想要修改对象时创建新值.请注意,Map模块创建的映射旨在支持此样式:add返回与旧对象共存的新对象(即,它是持久数据结构).切换到功能样式需要您更改count功能的界面; 无论如何,这是你想要做的事情,count通过将地图作为参数传递,能够在不同的地图上使用该功能.我推荐给 萨米的答案,例如代码很好的Ocaml风格.