Erlang學習筆記(四)模組與函式

畫船聽雨發表於2017-08-25

買的《Erlang程式設計》第二版終於到了,可以按照書中的章節記錄學習內容了。

1.模組

模組儲存在副檔名為.erl的檔案裡,必須先編譯再執行,編譯後的模組以.beam作為副檔名。
-export([area/1])的意思是帶有一個引數的函式area可以在此模組之外呼叫。
-import(lists, [map/2])的意思是map/2函式是從lists模組裡匯入的,這就意味著我們可以用map(Fun, ….)來代替lists:map的寫法了。

a. 逗號","分隔函式呼叫、資料結構和模式中的引數。
b. 分號";"分隔子句,例如在Case、If的使用中。
c. 句號“.”(後接空白)分隔函式整體,以及shell裡的表示式。

2.fun:基本的抽象單元

Erlang是一種函數語言程式設計語言。此外,函數語言程式設計語言還表示函式可以被用作其他函式的引數,也可以作為返回函式。操作其他函式的函式被稱為高階函式而在Erlang中用於代表函式的資料型別被稱為fun。
fun既可以作為函式也可以作為函式的引數被傳遞。
作為函式樣例:

 Double = fun(X) -> 2*X end.

fun作為函式的引數樣例:

L = [1,2,3,4].
lists:map(fun(x) -> 2*X end, L).

定義控制抽象樣例

-module(test_for).
-export([for/3]).

for(Max, Max, F) -> [F(Max)];%這裡表示當Max相同時將F(Max)的值加入到for中,這裡類似於C語言裡面的遞迴出口
for(I, Max, F)  -> [F(I)|for(I+1, Max, F)].%當I與Max不同時,將I與I以後分離
%簡單的來說就是先把1拿出來,通過第一行程式碼加入到F中,接著第二行程式碼把2拿出來,再通過第一行程式碼加入F中

這裡需要注意的是Erlang語言是函數語言程式設計,它的變數由於一旦繫結就無法更改所以沒有中間變數,所以像C++中的for(int i = 0; i < 10; i++)這種迴圈他是不存在的。它是通過遞迴呼叫的方式來實現迴圈的。在這個地方我曾糾結在不停的遞迴時I的值是否被重新賦予,是不是違反Erlang語言的語法規則?其實並不是,因為在遞迴呼叫的過程中,資料是儲存在一個遞迴棧之中的。所以每層遞迴數所儲存的位置是不同的,而在Erlang中,因為地址被繫結的原因導致了無法賦予新的值,在這裡看來是不矛盾的,故可以實現遞迴呼叫。
可以舉個C的例子來幫助理解:
這裡寫圖片描述

在這裡這個樣例就是對上述過程的一個類比分析,在這個直觀的C程式中,並沒有出現i++,這種情況。這是單純的通過遞迴呼叫的方式來實現1-10的求和這個過程。

3.簡單列表處理

在列表處理的過程中,我認為將列表分隔開為列表頭部與其他是非常重要的,因為Erlang是函式式的程式設計所以會導致的結果迴圈就要是要使用遞迴呼叫,這時候的拆分就可以很好地將列表“切開”,實現他的分別用,一般就是先處理頭部,然後在進行拼接,或者一直遞迴到最後一層再實現與前面的拼接。
注意的是,遞迴呼叫的出口。有時會用一個空的列表來表示遞迴的出口,就是類似於C++中的遞迴的最後一層下界。
列表處理簡答例子如下:

-module(test_sum).
-export([sum/1]).

sum([L|M]) -> L + sum(M);
sum([]) -> 0.

4.列表推導

列表推導是無需使用fun、map、filter就能實現建立列表的表示式。他可以是程式變的更短,更容易理解。
我感覺對於一個初學者來說,還是很不友好的啊,亂七八糟的指看著都彆扭。不過列表推導是真的好用,可以節省好多程式碼量,而且也很形象,感覺開發這門語言的大佬是真的隨意啊。
先舉個栗子:

L = [1,2,3,4,5].
lists:map(fun(X)) -> 2*X end, L).
[2*X || X <- L].%作用效果與第一行相同。

再舉個栗子:

-module(test_qsort).
-export([qsort/1]).%實現排序,過程是先把第一個拿出來然後比它小的在左邊,大於等於的在右邊,寫起來比C語言簡潔的太多了啊。

qsort([]) ->[];
qsort([Pivot | T]) -> 
                    qsort([X || X <- T,  X < Pivot])
                    ++[Pivot]++
                    qsort([X || X <- T, X >= Pivot]).

5.關卡

關卡(guard)是一種結構,可以用它來增加模式匹配的威力。使用關卡可以對某個模式裡的變數執行簡單的測試和比較。
關卡序列(guard sequence)是指單一或一系列的關卡,用”;”分隔,這是的”;”可以理解為邏輯“或”,所有的條件只要滿足一個既可以通過關卡。”,”在關卡里面的意思是表示為邏輯“與”只有所有的條件都滿足的時候才可以通過關卡。
來個栗子:

f(X, Y) when is_integer(X),  X > Y, Y < 6 -> X+Y. %","的樣例
X =:= dog; X =:= cat
is_integer(X), X > Y; abs(Y) < 23 %混合情況的樣例
....

6.歸集器

程式遍歷列表一次,把不同的引數放在合適的列表裡,這些列表就被稱為歸集器。
舉個栗子:

odd_and_evens1(L) ->
    Odds  = [X || X <- L, (X rem 2) =:= 1],
    Evens = [X || X <- L, (X rem 2) =:= 0],
    {Odd, Evens}.

在這個程式碼中,遍歷了列表兩次,當列表很長的時候就可能會出現問題,因此需要減少它的遍歷次數。

-module(test_odds_and_evens).
-export([odds_and_evens/1]).

odds_and_evens(L) -> odds_and_evens_acc(L, [], []).

odds_and_evens_acc([H|T], Odds, Evens) ->
    case (H rem 2) of
        1 -> odds_and_evens_acc(T, [H|Odds], Evens);
        0 -> odds_and_evens_acc(T, Odds, [H|Evens])
    end;
odds_and_evens_acc([], Odds, Evens) -> {Odds, Evens}.

這個程式就實現了一次遍歷所有的列表,但是也有個小問題,這裡利用的時候類似頭插法的方法,所以會導致資料的順序跟之前相反了,需要利用lists中的reverse方法來實現逆轉,這是內部函式。

7.部分習題解析

2.內建函式tuple_to_list(T)能將元組T裡的元素轉換成一個列表。輕編寫一個名為:my_tuple_to_list(T)的函式來做同樣的事情,但不要使用相同功能的內部的內建函式。

-module(test_my_tuple_to_list).
-export([my_tuplr_to_list/1]).

my_tuplr_to_list(T) -> 
    my_tuplr_to_list(T, tuple_size(T), 0, []).
my_tuplr_to_list(T, Index, F, L) when Index > F ->
    my_tuplr_to_list(T, Index-1, F, [element(Index, T) | L]);
my_tuplr_to_list(T, Index, F, L) when Index =:= F -> L.

7.向math_functions.erl新增一個返回{Even,Odd}的split(L)函式,其中Even是一個包含L裡所有偶數的列表,Odd是一個包含L裡所有奇數的列表。請使用兩種不同的方式編寫這個函式,一種使用歸集器,另一種使用在練習6中編寫的filter函式。

split(L) -> odds_and_evens_acc(L, [], []).

split([H|T], Odds, Evens) ->
    case (H rem 2) of
        1 -> split(T, [H|Odds], Evens);
        0 -> split(T, Odds, [H|Evens])
    end;
split([], Odds, Evens) -> {Odds, Evens}.
split(L) ->
    Odd  =  [X || X <- L, (X rem 2) =/= 0],
    Even =  [X || X <- L, (X rem 2) =:= 0],
    {Even, Odd}.

8.小結

剛開始接觸函式式的程式設計,所以有一些懵,還好找到了我大腿鵬哥,鵬哥很耐心的給我講解了一下如何理解函式式的程式設計。畢業多年還蒙受我鵬哥的照顧不勝感激啊。
現在說說我的理解,函數語言程式設計是不使用中間變數的,他是通過函式的呼叫返回值來實現的,資料的入口是通過引數來解決的,迴圈是通過不停的呼叫來實現的,鵬哥給我舉了個很形象的例子來理解,以求兩個數的絕對值的差為樣例來進行舉例,如下:
C語言的程式設計方式:
這裡寫圖片描述

在C語言中,我們求絕對值的差通過定義變數、比較變數、做運算,直接就可以得到結果。
函式式語言的實現方式:
這裡寫圖片描述

沒有中間變數的出現,而是各種函式的呼叫來實現這個功能,然後返回結果。不要糾結最後的int c那個定義只是為了好理解,在Eralng中就不會出現了。

相關文章