有一段時間沒有寫 Erlang 程式了,近日有個小夥伴要幫忙,給了我幾個小練習,讓我寫寫看。雖然寫得有點慢,畢竟還是完成了。突發靈感,Erlang有沒有什麼“程式設計套路”呢?
初學 Erlang 者大部分都感歎“太逆天了”,個人認為,掌握Erlang 程式設計邏輯兩個基本點就是模式比對和遞歸思想。而遞歸思想在任何一種程式設計語言中都是存在的,也是解決問題最簡便的思維方式。
就着這個練習,其實之前好像也有過一次接受過類似問題的求助,對照我的思考總結一下:
習題如下:
格式化元組:
輸入:[{money,200},{goods,1001,1},{goods,71143,9},{money,150},{rmb,600},{goods,71143,1},{goods,1001,1},{card,3001,281479271678954},card,4001,281479271678955}]
輸出:[{money,350},{goods,[{1001,2},{71143,10}]},{rmb,600},{card,[{3001,281479271678953},{4001,281479271678955}]}]
我的代碼如下圖(此程式沒有對輸入資料格式的檢查):

其基本思想是對要解決的問題進行分類,分别編寫一個子句,每個子句中的函數頭部括号中模式比對部分(參數部分)就是描述解決複雜問題的一種情況的入口(當然,還可以輔之以保護式),而函數體則是解決這一種情況下的所有處理語句。這些處理語句又大緻分為兩類:
1.可以直接傳回處理結果。這也是程式運作結束的情況,或者用遞歸的思想看就是退出遞歸的條件。
比如本例中的第51行代碼:
format2([]) -> [];
當傳入要格式化的清單為空時,直接傳回空的清單。
再如第54行代碼:
format2([],Lb) -> Lb;
元組清單中的元素全部處理完成後,直接傳回結果清單。
而代碼中61-71行中的combb函數則每個子句都是處理一類問題,并直接傳回處理結果的情況。
2.需要遞歸處理,以擷取最終結果。即經本輪處理後,要處理的問題減少了,但并沒有獲得最終結果,還是要經過多輪遞歸調用,将要處理的問題減少到0,即遞歸退出的條件,最終會傳回結果。
本例代碼中的combine函數(55-59)中的57、58行就是這種情況:從55行的模式比對就可以看出,通過模式比對逐個處理輸入元組清單中的元組,隻要沒有處理完,在57、58行中就會進行遞歸調用(此處正好對應在結果清單中可以找到有對應Key和無對應Key的兩種情況),每次遞歸調用就會處理掉所給清單中的一個元組,直到處理完成後會通過模式比對方式調用第54行的子句并傳回處理結果。
再看我解決這個問題的另一版本:
第30行就是上文中說的第一種情況,第32行就是遞歸調用;comba函數(35-44)子句中,前三個子句都是處理可直接傳回結果的,第四個子句(44行)則是遞歸調用,嘗試繼續比對。
還有一個套路補充下,就是通過增加函數的元數來接收處理結果。比如52行、58行、32行等。
最後,說明一下,寫 Erlang 函數時還要注意模式比對的順序,不要讓前面子句的模式比對擋住了後面子句。原則就是先做特殊比對,再做一般比對。
下面再來個粟子:
查詢List1是為List2的字首
先将問題分類:
1. 兩個清單都為空,結果為是;
2. 前一個清單為空,後一個不為空,結果為是;
3. 前一清單不為空,後一個為空,結果為否;
4. 兩個清單不為空,且第一個元素相同,需要繼續查詢(遞歸調用);
5. 兩個清單不為空,且第一個元素不同,結果為否。
對照下面的程式是不是就一目了然了呢?
不當之處,敬請斧正。