2015-10-07 154 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