2015-10-07 161 views
1

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

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

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

编辑:

我试图

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

,但它不知道什么wordMap是getCount将功能...

+0

? – cago

+0

这只是一个例子。对于我的实际功能,而不是长度,我有一个地图,我用来存储元素的出现次数,这意味着我必须做Map.empty才能得到一个空的地图,然后我必须通过添加来更新地图它。我不知道该怎么做。 – jstnchng

+2

在OCaml中,如果你可以:一般你应该避免突变:不要使用可变的'ref'计数器,而是使用'List.fold_left'或'fold_right'来解决这类问题。 – camlspotter

回答

2

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

学会用褶皱思考是功能编程的基本技能,所以值得学习。

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

为什么are'nt使用`List.length list`让你列表的长度(你绝对可以使用折叠积累的地图,同时遍历列表。)

+0

好!谢谢。我知道我在某个地方有一个错误,我只是不知道在哪里... – jstnchng

+0

我做了一个快速编辑,想知道你是否可以看看我?添加了我使用fold的尝试... – jstnchng

+0

通过参数来维护状态,而不是全局。要折叠的功能需要*两个*参数。如果使用'fold_left',第一个是当前状态(地图),第二个是列表的下一个元素。它返回一个新的地图。您的代码尝试使用全局值;这不起作用(它是不可变的)。 –

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变量。别担心,我们很快就会收回。但是您可以通过再次调用长度来看到,我们可以为参数l分配一个新值tl。但它是纯粹的,被认为是更好的做法。

好吧,差不多。

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

但是,函数调用可以优化,如果他们是尾调用。作为Wikipedia可以解释给你听:

尾调用是作为 程序的最后执行的操作的子程序调用。

在后来的代码,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 

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

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

+0

谢谢帕特我实际上有你一样的优化尾部递归,但我问这个问题,因为我还有其他的东西我正在尝试做,所以我试图通过一个更简单的例子来学习这个概念。对于我的实际功能,而不是长度,我有一个地图用于存储元素的出现,这意味着我必须做Map.empty来得到一个空的地图,然后我必须通过添加地图来更新地图。我不知道该怎么做。 – jstnchng

+0

@jstnchng,我不确定我理解你的问题。然后像@Jeffrey Scofield所说的那样,“fold”是你的朋友(基本上,它遍历并且你只是写出参数更新)。如果没有,请用代码示例更新你的问题,这样我们就可以看到你的精确的问题。 – PatJ

+0

我想b你和杰弗里提到过,我应该先尝试折叠。感谢您的帮助,我会更新结果! – jstnchng