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