OCaml 通过列表迭代更新值

OCaml update value through iteration of a list

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

的事情
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 是什么...

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

您有多种方法可以做到这一点。

更简单的方法(来自命令式世界的初学者通常更喜欢)是使用引用。引用是您可以合法改变的变量。

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 returns 当前在参考中的值和 := 允许您执行命令更新。

但是你不应该写那个代码

是的,我用的是粗体,这就是我对它的认真程度。基本上,当您可以依赖纯函数式编程时,您应该避免使用引用。即当没有side-effects.

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

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

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

嗯,差不多。

最后一段代码存在递归问题:每次调用都会往栈中添加(无用的)数据,遍历链表后,再进行加法。我们不希望那样。

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

a tail call is a subroutine call performed as the final action of a procedure.

在后面的代码中,对 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

现在我们有了一个简洁、纯粹且 easy-to-optimize 的代码来计算您的列表的长度。

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

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

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

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

(遍历list的时候肯定可以用fold来积累map。)