OCaml通过迭代列表更新值

时间:2015-10-07 07:54:54

标签: ocaml

我想知道如何通过列表的迭代来更新变量的值。例如,假设我想跟踪列表的变量数量。我可以做类似

的事情
let list = [1;2;3;4;5]
let length = 0 in
    let getCount elmt = 
        length = length+1 in 
    List.iter getCount list

但是我收到错误This expression has type 'a -> bool这是有道理的,因为在length = length+1我正在使用=进行比较。我该如何更新长度值?

编辑:

我试过

let wordMap =
let getCount word = 
    StringMap.add word (1000) wordMap in 
List.fold_left getCount StringMap.empty wordlist;;

但它不知道getCount函数中的wordMap是什么......

3 个答案:

答案 0 :(得分:2)

你有几种方法可以做到这一点。

更简单的方法(通常首选来自命令世界的初学者)是使用参考。引用是一个可以合法变异的变量。

let length l =
 let count = ref 0 in
 let getCount _ = (* _ means we ignore the argument *)
  count := !count + 1
 in
 List.iter getCount l;
 !count

正如您在此处所见,!count返回引用中当前的值,:=允许您执行命令式更新。

但你不应该写那段代码

是的,我正在使用大胆的,这是我的严肃态度。基本上,当您可以依赖纯函数编程时,应该避免使用引用。也就是说,当没有副作用时。

那么当你不被允许时如何修改变量呢?这就是递归的地方。检查一下:

let rec length l =
 match l with
 | [] -> 0
 | _::tl -> 1 + length tl

在该代码中,我们不再拥有count变量。别担心,我们很快就会收回。但是你可以看到,只需再次调用长度,我们就可以为参数tl分配一个新值l。然而,它是纯粹的,被认为是一种更好的做法。

嗯,差不多。

最后一个代码存在递归问题:每个调用都会将(无用的)数据添加到堆栈中,并且在通过列表之后,将执行添加。我们不希望这样。

但是,如果函数调用是尾调用,则可以对其进行优化。正如Wikipedia可以向您解释:

  

尾调用是作为a的最终动作执行的子程序调用   过程

在后面的代码中,对length的递归调用不是尾调用,因为+是函数的最终操作。通常的技巧是使用累加器来存储中间结果。我们称之为count

let rec length_iterator count l =
 match l with
 | [] -> count
 | _::tl -> length_iterator (count+1) tl
in
let length l = length_iterator 0 l

现在我们有一个整洁,纯粹且易于优化的代码来计算列表的长度。

因此,要回答标题中所述的问题:使用(尾部)递归函数进行迭代,并将可更新变量作为此函数的参数。

答案 1 :(得分:2)

@PatJ进行了很好的讨论。但在实际代码中,您只需使用折叠。折叠的目的正是您所要求的,在遍历列表时保持某种状态(您喜欢的任何类型)。

学习以折叠方式思考是函数式编程的基本技能,因此值得学习。

一旦你擅长弃牌,你可以根据具体情况决定你是否需要可变状态。几乎在所有情况下你都没有。

(在遍历列表时,您绝对可以使用折叠来累积地图。)

答案 2 :(得分:0)

如果目标是获取列表的长度,只需使用列表模块提供的功能:List.length。否则,OCaml中的变量永远不可变,并且您尝试做的事情在OCaml中是非法的而根本不起作用。但是,如果您确实需要更新某个值,请考虑使用ref(有关详细信息:http://www.cs.cornell.edu/courses/cs3110/2011sp/recitations/rec10.htm)。