我想知道如何通过列表的迭代来更新变量的值。例如,假设我想跟踪列表的变量数量。我可以做类似
的事情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是什么......
答案 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)。