Erlang學習筆記(四)模組與函式
買的《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中就不會出現了。
相關文章
- TS學習筆記(四):函式筆記函式
- Python學習筆記_函式_匯入模組Python筆記函式
- [Erlang 學習筆記] Erlang開發建議筆記
- Swift學習筆記第四篇(函式)Swift筆記函式
- Erlang學習筆記(五)記錄與對映組筆記
- Erlang學習筆記(三)Erlang基礎語法筆記
- TypeScript 學習筆記 — 函式中的型別(四)TypeScript筆記函式型別
- async函式學習筆記。函式筆記
- Go 函式 學習筆記Go函式筆記
- 分析函式(學習筆記)函式筆記
- 生成函式 學習筆記函式筆記
- Nginx 學習筆記--程式與模組Nginx筆記
- JavaScript學習筆記 - 原生函式JavaScript筆記函式
- Golang學習筆記-1.6 函式Golang筆記函式
- Oracle學習筆記(6)——函式Oracle筆記函式
- LoadRunner函式學習筆記函式筆記
- MYSQL學習筆記14: 函式MySql筆記函式
- 函式學習四函式
- 深度學習——loss函式的學習筆記深度學習函式筆記
- Nginx 學習筆記--程式與模組(二)Nginx筆記
- Erlang學習筆記(二)Erlang基礎語法之If、Case的使用筆記
- python學習筆記(六)——函式Python筆記函式
- OpenCV學習筆記(4)——mixChannels函式OpenCV筆記函式
- OpenCV學習筆記(5)——normalize函式OpenCV筆記ORM函式
- Flutter學習筆記(4)--Dart函式Flutter筆記Dart函式
- js純函式學習筆記(一)JS函式筆記
- C++學習筆記(二)——函式C++筆記函式
- c語言學習筆記===函式C語言筆記函式
- 【C#學習筆記】函式呼叫C#筆記函式
- oracle學習筆記8: 分析函式Oracle筆記函式
- 學習筆記-----一時間函式筆記函式
- MYSQL學習筆記7: 聚合函式MySql筆記函式
- 《C++ Primer》學習筆記(六):C++模組設計——函式C++筆記函式
- Erlang學習筆記(七)二進位制型與位語法筆記
- PHP 第四周函式學習記錄PHP函式
- PHP 第八週函式學習筆記PHP函式筆記
- 學習筆記:javascript中的Generator函式筆記JavaScript函式
- JavaScript學習筆記(七)—— 再說函式JavaScript筆記函式