Eralng學習
1.配置D:\Program Files\erl5.9.1\bin的時候,將bin目錄下面的工具引入到環境變量裏面,然後可以利用
打開命令行控制窗口,先進到test.erl的位置,然後執行erl工具,詳細見截圖:
2.記得執行的時候要末尾要加上一個局點".",代表執行。
如果你想退出Erlang終端窗口,那麼請輸入q()並加上句點
3.基元由小寫字母開始或者是由單引號界定。當基元用小寫字母開始的時候,字母、數字、"at"符號(@).和下劃線(_)都是有效的字符,如果一個基元通過單引號
封裝起來,則可以使用任意字符。 (注:大寫字母不能當做基元來操作)
基元(atom)來表示文字常量。Erlang中的基元和其他語言中的枚舉作用是一樣的。
6> '1
6> 2
6> 3
6> 4'.
'1\n2\n3\n4'
上面的列子說明 單引號裏面的字符串可以換行來執行
4.rlang中有各種各樣的內置函數,通常在Erlang社區中稱爲BIF。
它們可以應用在程序和終端中。其中內置函數is_boolean用來測試一個Erlang值是否是布爾類型。
is_boolean(9+6).
5.布爾類型的邏輯運算符
((1>0) and (2>1)).
6.元組是用來保存一組數據元素的複合數據類型,其中數據元素要求是Erlang數據類型,但並不一定要求是相同的類型。
元組使用封閉的花括號{...}來表示,而其中的元素由逗號隔開。
Erlang提供了一些內置函數用來設置和檢索元組元素的內容,並得到元組的大小。
當一個元組的第一個元素是一個基元時,稱它爲標記(tag)。
這一Erlang慣例用來表示不同類型的數據,同時通常在使用它的程序中有着特殊的意義。
例如,在元組{person, 'Joe', 'Armstrong'}中,
基元person是標記,這有可能也就表明第二個元素是這個人的名,然後第三個元素是這個人的姓。
1> tuple_size({abc, {def, 123}, ghi}).
3
2> element(2,{abc, {def, 123}, ghi}).
{def,123}
3> setelement(2,{abc, {def, 123}, ghi},def).
{abc,def,ghi}
4> {1,2}<{1,3}.
true
5> {2,3}<{2,3}.
false
6> {1,2}=={2,3}.
false
從第二條可以看出,下標是從1開始,而不是0
(找一份Erlang的api)
7.列表和元組都是用來存儲元素集合的,在這兩種情況中,它們的元素都可以是不同的。
列表用封閉的括號[...]來定義,而它們的元素由逗號隔開。
[january, february, march]
[123,def, abc]
[a,[b,[c,d,e],f],g]
[]
[{person,'Joe','Armstrong'},{person,'Robert','Virding'},
{person, 'Mike', 'Williams'}]
[72,101,108,108,111,32,87,111,114,108,100]
[$H,$e,$l,$l,$o,$,$W,$o,$r,$l,$d]
"Hello World"
字符和字符串:
字符(a,b,c類似形式) 字符串("abc","bbbn"類型形式)
字符由整數來表示,字符串(由字符組成)則由整數列表來表示。字符的整數值可以通過在字母前加上$符號來得到:
1> $A.
65
2> $A + 32.
97
3> $a.
97
Erlang中沒有字符串數據類型。在Erlang中,字符串是一個由ASCII值組成的整數列表,並使用雙引號("")來表示。
因此,字符串"Hello World"實際上就是列表[72,101,108,108,111,32,87,111,114,108,100] 。
如果你使用ASCII整數符號$Character來表示整數,那麼你就會得到這樣的列表[$H,$e,$l,$l,$o,$ ,$W,$o,$r,$l,$d]。
空字符串""則等價於空列表[]:
基元和字符串的區別
基元和字符串的區別在哪裏?首先它們處理的方式不同,唯一可用於基元的操作是比較操作,而你可以使用很多不同的方式處理字符串。
例如,可以把字符串"Hello World"分解成["Hello","World"]的列表,而基元'Hello World'就不能這樣處理。
雙引號表示字符串。單引號表示基元
列表的組成和處理
正如我們前面所說,列表和元組的處理方式是非常不同的。元組的處理只能是提取的具體元素,而列表只要不爲空,就可以把一個列表分成頭部和尾部。
列表的頭部是指列表的第一個元素,它的尾部是一個包含所有剩餘元素的一個列表,而這個列表可以繼續這樣分解下去,如圖2-1所示。
列表的頭可以是任何東西,但是列表的尾通常還是一個列表。
正如列表可以如此這樣分割下去一樣,我們也可以用一個列表和一個元素組成一個新的列表。新的列表可以這樣構造 —— [頭部|尾部],這是一個典型的構造器(constructor,簡寫爲cons)。
關於列表的很多操作都在lists庫模塊中定義,下面我們可以看到其中的一些例子。
這些函數不是內置函數,它們通過在函數前加上模塊的名字來調用,並用冒號隔開,例如 lists:split。
列表函數和操作:
1> lists:max([1,2,3]).
3
2> lists:reverse([1,2,3]).
[3,2,1]
3> lists:sort([2,1,3]).
[1,2,3]
4> lists:split(2,[3,4,10,7,9]).
{[3,4],[10,7,9]}
5> lists:sum([3,4,10,7,9]).
33
6> lists:zip([1,2,3],[5,6,7]).
[{1,5},{2,6},{3,7}]
7> lists:delete(2,[1,2,3,2,4,2]).
[1,3,2,4,2]
8> lists:last([1,2,3]).
3
9> lists:member(5,[1,24]).
false
10> lists:member(24,[1,24]).
true
11> lists:nth(2,[3,4,10,7,9]).
4
12> lists:length([1,2,3]).
** exception error: undefined function lists:length/1
13> length([1,2,3]).
3
如果你想在一個列表前添加一個元素,那麼有兩種方法:
直接使用構造器,例如[1|[2,3,4]]。
使用++運算符,例如[1] + + [2,3,4]。
這兩種方法產生同樣的結果,但++運算符效率更低,並可能導致程序運行時速度大幅度減慢。
因此當你想添加一個元素到一個列表頭部的時候,應該儘量使用高效率的構造器方法([...|...])。
當++運算符加入到Erlang語言之後,程序員就從使用append函數轉變爲濫用++運算符。
其實++運算符和append函數都是代價很高的操作,因爲表達式的左邊的列表都要遍歷一次。
它們不僅僅是耗時的操作,而且常常是多餘的,比如在Erlang中所有I/O函數(包括套接字操作)都接受未展開的字符串,比如["Hello ",["Concurrent "]|"World"]。
列表:
List = [Element|List] or []
[[3,2]|1]
[1|[2,3]]==[1,2,3] true [1,2|3]==[1,2,3] false
[1|[2|[3]] 應該少了一個]號確實少了個 正確的應該是 [1|[2|[3]]]
[1|[2|[3|[]]]]
[1,2|[3|[]]]
[3]==[3|[]]. true
[3]==[[]|[3]]. false
[3|[]]==[3]. true
[[]|[3]]==[3]. false
8.項元比較.
布爾類型:
==等於 /==不等於
=:= 精確等於 =/=精確不等於
>= 大於等於 > 大於 <= <
number < atom < reference < fun < port < pid < tuple < list < binary
列表按字典順序排列,就像是字典裏面的詞一樣。第一個元素先比較,小的那個表明這個列表也較小:如果它們是相同的,則第二個元素將繼續進行比較,如此下去。當其中一個列表先比較完了,那麼它就是較小的列表。因此:
5> [boo,hoo]<[adder,zebra,bee].
false
6> [boo,hoo]<[boo,hoo,adder,zebra,bee].
true
而另一方面,元組比較時,先會比較結構中的元素數目,然後再一個個比較各個元素的值:
7> {boo,hoo}<{adder,zebra,bee}.
true
8> {boo,hoo}<{boo,hoo,adder,zebra,bee}.
true
9.變量
變量用來存儲簡單和複合數據類型的值。在Erlang中,變量必須以大寫字母開頭(注2),後面的字符可以是大小寫字母、整數和下劃線。
它們不能包含任何其他的“特殊”字符。下面是變量的一些例子:
A_long_variable_name Flag Name2 DbgFlag
Erlang中的變量不同於大多數的傳統編程語言。
在一個變量的生命週期內,包括在Erlang終端處理過程中,只容許給變量賦值一次,你就不能改變它了,這稱爲單次賦值。
因此,當你需要對一個變量的值進行計算和操作的時候,可以把結果存儲在另外一個新的變量當中。
Erlang中變量的另一個有用的特性是我們不需要聲明它們,只需要使用它們即可。
rlang的一個優點是,它不需要明確地分配和釋放內存。對於C程序員來說,這意味着不會再有不眠之夜來查找指針錯誤或者內存泄漏。
用來存儲複雜數據結構的內存在需要的時候會由系統自動分配,而當不再引用它的時候,就會由垃圾收集器自動回收釋放掉。
Erlang內存管理:
現有的Erlang虛擬機的實現使用了通用繼承性垃圾收集器。
垃圾收集針對每個併發進程各自獨立進行:當一個處理器沒有更多的內存用於存儲的時候,就會自動觸發垃圾收集器。
10.匹配模式: (重點測試下這種模式 測試 xs=[[]|XS])
模式匹配[A,B,C,D] = [1,2,3]則會失敗。雖然它們兩個都是列表類型,但是左邊的列表有4個元素,而右邊一個列表只有3個元素。
一個常見的誤解是D可以設置爲空列表,然後模式匹配應該成功。在這個例子中這是不可能的,因爲C和D之間由逗號而不是構造器操作符分隔開。
[A,B,C|D]=[1,2,3]的模式匹配就會成功,變量A、B和C分別綁定到整數1、2和3,變量D則綁定到尾部,在這裏就是空列表。
{A, _, [B|_], {B}} = {abc, 23, [22, 23], {22}}
會成功地提取元組的第一個元素,即基元abc,並把它綁定到變量A上。同時也將成功提取第三個元素元組的第一個元素並把它綁定到變量B上。
14> Var = {person, "Francesco", "Cesarini"}.
{person, "Francesco", "Cesarini"}
15> {person, Name, Surname} = Var.
{person, "Francesco", "Cesarini"}
在第一個語句中我們把一個類型爲person的元組綁定到變量Var上了,然後在第二個語句中我們分別提取了名字和姓氏。
這將會成功地把字符串“Francesco”綁定到變量Name上並把字符串“Cesarini”綁定到變量Surname上。
11> {Element, Element, _} = {1,1,2}.
{1,1,2}
就如使用變量一樣,在一個模式中我們也可以使用通配符,_,。這將匹配任何東西,並且不產生任何綁定。
我們剛纔看到了變量可以用下劃線開始,下劃線是一個特殊的標記,它告訴編譯器,這些變量是無關緊要的,它們只是作爲程序中不需要的變量的佔位符。
“無關緊要的”變量的功能跟普通變量一樣,可以檢查、使用和比較它們的值。唯一不同的是,普通變量的值如果從未使用過,編譯器將會發出警告,而使用“無關緊要的”變量則不會。使用“無關緊要的”變量是一種很好的編程實踐,這告訴閱讀代碼的人應該忽略掉這個值。
爲了提高可讀性和維護性,程序員經常以“無關緊要的”變量的形式引入值和類型。
下劃線本身也是個“無關緊要的”變量,但不能訪問其內容:因爲它的值被忽略了而且從未綁定。
11.函數
Erlang程序包含相互調用的函數。函數組合在一起並在模塊中定義。函數的名稱是一個基元。
一個函數的頭包括名字,隨後是一對括號,在裏面包含多個形式的參數或者沒有參數。
在Erlang中,函數參數的數量叫做元數。使用箭頭(->)來分隔函數頭和函數主體。
在這兩種操作系統下,你可以在Erlang終端中使用cd(Directory)進入到各個目錄。
一旦進入該目錄,你就可以在Erlang的終端中使用c(Module)並省略名稱中的.erl後綴來進行編譯。
如果代碼中沒有錯誤,編譯就會成功。
factorial(0) -> 1;
factorial(N) ->
N * factorial(N-1).
正如我們前面提到過的,模式匹配發生在函數的頭部,如何匹配成功,就會綁定變量N的一個實例。對每個語句來講變量都是本地的,它們不需要分配或釋放,Erlang運行時系統會自動處理這些。(方法定義的時候,前面先用;來表示,最後一個方法用.來表示)
12.模塊:
函數組合在一起構成了模塊。一個程序往往是分散在幾個模塊當中,每個模塊包括按邏輯組合在一起的函數。
模塊文件以.erl後綴來結尾,文件名稱和模塊名稱必須是相同的。
若要執行從一個模塊中導出的函數,你必須編譯代碼,這會在與模塊相同的目錄下面生成一個module.beam文件:
模塊可以這樣直接命名–module(Name),因此在例2-2中,demo模塊將存儲在一個名爲demo.erl的文件中。
-module(demo).
-export([double/1]).
% This is a comment.
% Everything on a line after % is ignored.
double(Value) ->
times(Value, 2).
times(X, Y) ->
X*Y.
export指令以Function/Arity的格式(方法名/參數個數),包含了導出函數的一個列表。這些函數是全局性的,這意味着可以從模塊的外部調用它們。
在Erlang中,註釋以百分號(%)開始直到該行結束。請務必在代碼中多多使用它們!
在Windows環境下,一種打開werl終端的方法是右擊一個.beam文件,然後從彈出的窗口菜單中選擇Open With option的“werl”選項。從現在起,雙擊任何源文件相同目錄下的.beam文件,就會打開一個Erlang終端了。
在這兩種操作系統下,你可以在Erlang終端中使用cd(Directory)進入到各個目錄。一旦進入該目錄,你就可以在Erlang的終端中使用c(Module)並省略名稱中的.erl後綴來進行編譯。如果代碼中沒有錯誤,編譯就會成功。
1> cd("/home/francesco/examples").
/home/francesco/examples
ok
2> c(demo).
{ok,demo}
3> demo:double(10).
20
4> demo:times(1,2).
** exception error: undefined function demo:times/2
13.從複合數據類型中提取值
Person = {person, "Mike", "Williams", [1,2,3,4]}.
{person, Name, Surname, Phone} = Person.
Name.
小寫字母開頭表示基元,大寫字母開頭表示變量
14.case
防禦性編程
假設程序把一個整數映射到一個表示一週中某天的基元。使用catch-all語句的防錯性編程如下所示。
convert(Day) ->
case Day of
monday -> 1;
tuesday -> 2;
wednesday -> 3;
thursday -> 4;
friday -> 5;
saturday -> 6;
sunday -> 7;
Other -> {error, unknown_day}
end.
listlen(Y) ->
case Y of
[] -> 0;
[_|Xs] -> 1 + listlen(Xs)
end.
if case X rem 2 of
X rem 2 == 1 -> odd; 1 -> odd;
X rem 2 == 0 -> even 0 -> even
end end
15.保護元
保護元(guard)是一個額外的限制條件,它應用於函數的case或者receive語句中(我們會在第4章中具體講述receive表達式)。
保護元應該放在“->”之前來分隔語句的主體和頭部。
保護元由when關鍵字和緊跟其後的一個保護元表達式組成。只有在模式匹配和保護元表達式求值結果爲基元true的情況下,這個語句纔會執行。
max(X,Y) when X>Y- > X;
max(X,Y) - > Y.
接下來重寫第2章中階乘的例子:
factorial(0) -> 1;
factorial(N) ->
N * factorial(N-1).
這一次使用保護元:
factorial(N) when N > 0 ->
N * factorial(N - 1);
factorial(0) -> 1.
my_add(X,Y) when not(((X>Y) or not(is_atom(X))
) and (is_atom(Y) or (X==3.4))) ->
X+Y.
新的保護元函數是is_atom/1和is_integer/1等。
元編程:
一個函數在運行時才確定將調用哪些函數的特性叫做元編程,也就是程序創建程序並運行。
16.內置函數:
要從標準輸入讀取一行,請使用io:get_line/1,它需要提示符字符串(或基元)作爲它的輸入:
1> io:get_line("gissa line>").
gissa line>lkdsjfljasdkjflkajsdf.
"lkdsjfljasdkjflkajsdf.\n"
也可以這樣讀取指定數量的字符:
2> io:get_chars("tell me> ",2).
tell me> er
"er"
17.
keysearch(Key, N, TupleList) -> {value, Tuple} | false
這個函數和keyfind差不多,就是返回值的結構不一樣
也是在TupleList中找一個Tuple,這個Tuple的第N個元素和Key一樣。
例子:
List1 = [{name,"zhangjing"},{name,"zhangsan"}]
lists:keysearch("zhangjing",2,List1).
結果:
{value,{name,"zhangjing"}}
lists:keysearch(1,1,[{1,2},{2,4}]).
第一個元素的值和key值一樣。
18.匹配模式,函數匹配模式
Page40 模塊是Erlang中代碼的基本單元,我們編寫的所有函數都存於模塊之中。模塊文件通常以.erl爲擴展名的文件中。
要運行一個模塊,首先需要編譯它,編譯成功之後的模塊文件其擴展名爲.beam
模塊名:方法名(). 來調用模塊裏面的方法
小寫字母是基元,大寫字母是變量
函數cost/1 :符號Name/N表示一個帶有N個參數名爲Name的函數,N稱爲函數的運算目。
函數cost/1必須從模塊之中導出,如果你想從模塊的外部調用它,這是必須的。即用-compile(export_all)來導出模塊之中的所有函數
[x]就是[x|[]]的縮寫
19.fun
P47。fun就是匿名函數,先定義一個Z,然後將fun賦值給一個變量Z (基元是小寫字母開頭的,就當做是一個常量,變量大寫字母開頭的,相當於一個變量)
1>Z=fun(X)-> 2*X end.
2>Z(2)
4
fun匹配多個函數 p48
20. 逗號(,)用來分隔函數調用、數據構造器以及模式中的參數
分號(;)用來分隔子句,在這幾種情況相下都會用到子句:分段的函數定義、case語句、if語句、try....catch語句以及receive表達式。
無論何時,我們只看到一組後面跟有表達式的模式,都會使用分號進行分隔。
div 整數除法
rem 整數取餘
21.返回fun的fun的值的時候,
Double=fun(x)->(2*x)end.
Double(5).
10
%定義兩層fun
Mult= fun(Time)->(fun(X)->(Time*X)) end.
調用它的時候, Trip=Mult(5). Time的值爲5
Trip(3). X的值爲3
(1.)Mult=fun(time,A,B)->A*B end.
調用 Mult(time,2,3) 返回6
(2.)Mult(A,B)-> A*B end.
調用 Mult(3,4) 返回12
求和:
sum(H|T)-> H+sum(T);
sum([])->0.
將此文件儲存爲mylists.erl
c(mylists.erl). %%先將此模塊編譯
然後再調用
L=[1,2,3]
mylists.sum(L).
6
map(_,[]) ->[];
map(F,[H|T]) ->[F(H)|map(F,T)]. %[F[H]|F[T]|[]] 將依次的列表元素當做參數傳進方法裏面去 (F變量代表的是一個方法名)
L=[1,2,3,4]
mylists.map(fun(x)->2*x end,L). %% fun(1)+fun(2)+fun(3)+fun(4)
解析:
[2,4,6,8]
注:不能將自己的模塊定義成lists ,因爲有系統的模塊名爲這個名字
模塊 -import(lists,[map/2,sum/1]).
如果有導入聲明的時候,不需要寫lists:map(...),可以直接使用map(....), 如果沒有導入的函數,記得要加上模塊名才能使用
23.
列表解析: (列表解析很有作用)
L=[1,2,3,4]
[2*X||X<-L]. %[F(X)||X<-L]代表"由F(X)組成的列表,其中X是取值於列表L"
使用列表解析實現過濾功能:
[X||{a,X}<-{a,1},{1,mn},{a,'m'}]
[1,'m']
使map方法更簡潔:
map(F,L)->[F(X)||X<-L]. %<-不是方法體,而是說X的值是從L裏面取的
列表解析很強大!!
list.seq(1,N)返回一個由1到N整數組成的列表,所以A<-lists:seq(1,N)意味着A的取值範圍是1到N的所有整數。
max(X,Y) ->
case X>Y of
true->X;
false->Y;
end.
max(X,Y) ->
if X>Y
true - >X;
false ->Y;
end.
運用保護元:
max(X,Y) when X>Y -> X;
max(X,Y) ->Y.
什麼情況下使用end結尾??
斷言序列 ==保護元 Page58
24.記錄
Page60.
-record(Name,{key1=Default1,
key2=Default2,
.....
}).
定義record
-record(todo,{status=remider,who=joe,text}
})
創建和更新記錄
X=#todo{}.
X1=#todo{status=urgent,text='Fix errata in book'}.
X2=X1#todo{status=done}.
#用於創建類型爲todo的新記錄,key都是原子。
X2=X1#todo{status=done}. 這個表示創建了一個X1(X1必須爲todo類型的)的副本,並將value的值改爲了done,原記錄本身並未改變
記錄的匹配模式:
#todo{who=W,text=Txt} =X2.
W.
joe
Txt.
Fix errata in book
%%匹配操作符的左邊,我們使用了自由變量W和Txt來定義一個記錄模式
如果只是想提取某個記錄之中的對應的值,X2#todo.text來取值
-module(shop).
-export(cost).
cost(oranges) ->5;
cost(apple) ->8;
cost(milk) ->16.
-module(shop1).
-export([total/1]).
total([{What,N}|T]) -> shop:cost(What)*N+total(T);
total([]) -> 0.
嘗試在Erlang終端輸入xd ,進入到相應的目錄
在這兩種操作系統下,你可以在Erlang終端中使用cd(Directory)進入到各個目錄。一旦進入該目錄,你就可以在Erlang的終端中使用c(Module)並省略名稱中的.erl後綴來進行編譯。如果代碼中沒有錯誤,編譯就會成功。
-module(demo).
-export([double/1]).
% This is a comment.
% Everything on a line after % is ignored.
double(Value) ->
times(Value, 2).
times(X, Y) ->
X*Y.
1> cd("/home/francesco/examples").
/home/francesco/examples
ok
2> c(demo).
{ok,demo}
3> demo:double(10).
20
4> demo:times(1,2).
** exception error: undefined function demo:times/2
25.順序型編程進階
二進制數據:在Erlang中可以使用一種二進制數據結構來存儲大量的原始數據。相對於列表或者元組,二進制類型更加節省內存,而且運行時系統也對此進行了優化。
在書寫和打印時,二進制數據以一個整數或者字符序列的形式了來出現,兩端分別用兩個小於號和兩個大於號括起來,例如:
<<5,10,20>>.
<<5,10,20>>
<<"hello">>.
<<"hello">>
列表構造:[1,2,[3,4,5],6|[8]] (記得測試下)
比特語法:
M=<<X:3,Y:6,Z:7>>.
atom_to_list([hello]).
"hello"
[2*X||X<-[1,2,3]]
fileName .hrl包含的文件。
-include("lerne/file.hrl").
短路布爾表達式:
Expr1 orelse Expr2 %%Expr1爲true的時候,不會對Expr2求值, 只有當Expr1爲false的時候,纔會對Expr2求值。
Expr1 andalso Expr2 %%Expr1爲true的時候,Expr2必須被求值, 只有當Expr1爲false的時候,纔不會對Expr2求值。
打開命令行控制窗口,先進到test.erl的位置,然後執行erl工具,詳細見截圖:
2.記得執行的時候要末尾要加上一個局點".",代表執行。
如果你想退出Erlang終端窗口,那麼請輸入q()並加上句點
3.基元由小寫字母開始或者是由單引號界定。當基元用小寫字母開始的時候,字母、數字、"at"符號(@).和下劃線(_)都是有效的字符,如果一個基元通過單引號
封裝起來,則可以使用任意字符。 (注:大寫字母不能當做基元來操作)
基元(atom)來表示文字常量。Erlang中的基元和其他語言中的枚舉作用是一樣的。
6> '1
6> 2
6> 3
6> 4'.
'1\n2\n3\n4'
上面的列子說明 單引號裏面的字符串可以換行來執行
4.rlang中有各種各樣的內置函數,通常在Erlang社區中稱爲BIF。
它們可以應用在程序和終端中。其中內置函數is_boolean用來測試一個Erlang值是否是布爾類型。
is_boolean(9+6).
5.布爾類型的邏輯運算符
((1>0) and (2>1)).
6.元組是用來保存一組數據元素的複合數據類型,其中數據元素要求是Erlang數據類型,但並不一定要求是相同的類型。
元組使用封閉的花括號{...}來表示,而其中的元素由逗號隔開。
Erlang提供了一些內置函數用來設置和檢索元組元素的內容,並得到元組的大小。
當一個元組的第一個元素是一個基元時,稱它爲標記(tag)。
這一Erlang慣例用來表示不同類型的數據,同時通常在使用它的程序中有着特殊的意義。
例如,在元組{person, 'Joe', 'Armstrong'}中,
基元person是標記,這有可能也就表明第二個元素是這個人的名,然後第三個元素是這個人的姓。
1> tuple_size({abc, {def, 123}, ghi}).
3
2> element(2,{abc, {def, 123}, ghi}).
{def,123}
3> setelement(2,{abc, {def, 123}, ghi},def).
{abc,def,ghi}
4> {1,2}<{1,3}.
true
5> {2,3}<{2,3}.
false
6> {1,2}=={2,3}.
false
從第二條可以看出,下標是從1開始,而不是0
(找一份Erlang的api)
7.列表和元組都是用來存儲元素集合的,在這兩種情況中,它們的元素都可以是不同的。
列表用封閉的括號[...]來定義,而它們的元素由逗號隔開。
[january, february, march]
[123,def, abc]
[a,[b,[c,d,e],f],g]
[]
[{person,'Joe','Armstrong'},{person,'Robert','Virding'},
{person, 'Mike', 'Williams'}]
[72,101,108,108,111,32,87,111,114,108,100]
[$H,$e,$l,$l,$o,$,$W,$o,$r,$l,$d]
"Hello World"
字符和字符串:
字符(a,b,c類似形式) 字符串("abc","bbbn"類型形式)
字符由整數來表示,字符串(由字符組成)則由整數列表來表示。字符的整數值可以通過在字母前加上$符號來得到:
1> $A.
65
2> $A + 32.
97
3> $a.
97
Erlang中沒有字符串數據類型。在Erlang中,字符串是一個由ASCII值組成的整數列表,並使用雙引號("")來表示。
因此,字符串"Hello World"實際上就是列表[72,101,108,108,111,32,87,111,114,108,100] 。
如果你使用ASCII整數符號$Character來表示整數,那麼你就會得到這樣的列表[$H,$e,$l,$l,$o,$ ,$W,$o,$r,$l,$d]。
空字符串""則等價於空列表[]:
基元和字符串的區別
基元和字符串的區別在哪裏?首先它們處理的方式不同,唯一可用於基元的操作是比較操作,而你可以使用很多不同的方式處理字符串。
例如,可以把字符串"Hello World"分解成["Hello","World"]的列表,而基元'Hello World'就不能這樣處理。
雙引號表示字符串。單引號表示基元
列表的組成和處理
正如我們前面所說,列表和元組的處理方式是非常不同的。元組的處理只能是提取的具體元素,而列表只要不爲空,就可以把一個列表分成頭部和尾部。
列表的頭部是指列表的第一個元素,它的尾部是一個包含所有剩餘元素的一個列表,而這個列表可以繼續這樣分解下去,如圖2-1所示。
列表的頭可以是任何東西,但是列表的尾通常還是一個列表。
正如列表可以如此這樣分割下去一樣,我們也可以用一個列表和一個元素組成一個新的列表。新的列表可以這樣構造 —— [頭部|尾部],這是一個典型的構造器(constructor,簡寫爲cons)。
關於列表的很多操作都在lists庫模塊中定義,下面我們可以看到其中的一些例子。
這些函數不是內置函數,它們通過在函數前加上模塊的名字來調用,並用冒號隔開,例如 lists:split。
列表函數和操作:
1> lists:max([1,2,3]).
3
2> lists:reverse([1,2,3]).
[3,2,1]
3> lists:sort([2,1,3]).
[1,2,3]
4> lists:split(2,[3,4,10,7,9]).
{[3,4],[10,7,9]}
5> lists:sum([3,4,10,7,9]).
33
6> lists:zip([1,2,3],[5,6,7]).
[{1,5},{2,6},{3,7}]
7> lists:delete(2,[1,2,3,2,4,2]).
[1,3,2,4,2]
8> lists:last([1,2,3]).
3
9> lists:member(5,[1,24]).
false
10> lists:member(24,[1,24]).
true
11> lists:nth(2,[3,4,10,7,9]).
4
12> lists:length([1,2,3]).
** exception error: undefined function lists:length/1
13> length([1,2,3]).
3
如果你想在一個列表前添加一個元素,那麼有兩種方法:
直接使用構造器,例如[1|[2,3,4]]。
使用++運算符,例如[1] + + [2,3,4]。
這兩種方法產生同樣的結果,但++運算符效率更低,並可能導致程序運行時速度大幅度減慢。
因此當你想添加一個元素到一個列表頭部的時候,應該儘量使用高效率的構造器方法([...|...])。
當++運算符加入到Erlang語言之後,程序員就從使用append函數轉變爲濫用++運算符。
其實++運算符和append函數都是代價很高的操作,因爲表達式的左邊的列表都要遍歷一次。
它們不僅僅是耗時的操作,而且常常是多餘的,比如在Erlang中所有I/O函數(包括套接字操作)都接受未展開的字符串,比如["Hello ",["Concurrent "]|"World"]。
列表:
List = [Element|List] or []
[[3,2]|1]
[1|[2,3]]==[1,2,3] true [1,2|3]==[1,2,3] false
[1|[2|[3]] 應該少了一個]號確實少了個 正確的應該是 [1|[2|[3]]]
[1|[2|[3|[]]]]
[1,2|[3|[]]]
[3]==[3|[]]. true
[3]==[[]|[3]]. false
[3|[]]==[3]. true
[[]|[3]]==[3]. false
8.項元比較.
布爾類型:
==等於 /==不等於
=:= 精確等於 =/=精確不等於
>= 大於等於 > 大於 <= <
number < atom < reference < fun < port < pid < tuple < list < binary
列表按字典順序排列,就像是字典裏面的詞一樣。第一個元素先比較,小的那個表明這個列表也較小:如果它們是相同的,則第二個元素將繼續進行比較,如此下去。當其中一個列表先比較完了,那麼它就是較小的列表。因此:
5> [boo,hoo]<[adder,zebra,bee].
false
6> [boo,hoo]<[boo,hoo,adder,zebra,bee].
true
而另一方面,元組比較時,先會比較結構中的元素數目,然後再一個個比較各個元素的值:
7> {boo,hoo}<{adder,zebra,bee}.
true
8> {boo,hoo}<{boo,hoo,adder,zebra,bee}.
true
9.變量
變量用來存儲簡單和複合數據類型的值。在Erlang中,變量必須以大寫字母開頭(注2),後面的字符可以是大小寫字母、整數和下劃線。
它們不能包含任何其他的“特殊”字符。下面是變量的一些例子:
A_long_variable_name Flag Name2 DbgFlag
Erlang中的變量不同於大多數的傳統編程語言。
在一個變量的生命週期內,包括在Erlang終端處理過程中,只容許給變量賦值一次,你就不能改變它了,這稱爲單次賦值。
因此,當你需要對一個變量的值進行計算和操作的時候,可以把結果存儲在另外一個新的變量當中。
Erlang中變量的另一個有用的特性是我們不需要聲明它們,只需要使用它們即可。
rlang的一個優點是,它不需要明確地分配和釋放內存。對於C程序員來說,這意味着不會再有不眠之夜來查找指針錯誤或者內存泄漏。
用來存儲複雜數據結構的內存在需要的時候會由系統自動分配,而當不再引用它的時候,就會由垃圾收集器自動回收釋放掉。
Erlang內存管理:
現有的Erlang虛擬機的實現使用了通用繼承性垃圾收集器。
垃圾收集針對每個併發進程各自獨立進行:當一個處理器沒有更多的內存用於存儲的時候,就會自動觸發垃圾收集器。
10.匹配模式: (重點測試下這種模式 測試 xs=[[]|XS])
模式匹配[A,B,C,D] = [1,2,3]則會失敗。雖然它們兩個都是列表類型,但是左邊的列表有4個元素,而右邊一個列表只有3個元素。
一個常見的誤解是D可以設置爲空列表,然後模式匹配應該成功。在這個例子中這是不可能的,因爲C和D之間由逗號而不是構造器操作符分隔開。
[A,B,C|D]=[1,2,3]的模式匹配就會成功,變量A、B和C分別綁定到整數1、2和3,變量D則綁定到尾部,在這裏就是空列表。
{A, _, [B|_], {B}} = {abc, 23, [22, 23], {22}}
會成功地提取元組的第一個元素,即基元abc,並把它綁定到變量A上。同時也將成功提取第三個元素元組的第一個元素並把它綁定到變量B上。
14> Var = {person, "Francesco", "Cesarini"}.
{person, "Francesco", "Cesarini"}
15> {person, Name, Surname} = Var.
{person, "Francesco", "Cesarini"}
在第一個語句中我們把一個類型爲person的元組綁定到變量Var上了,然後在第二個語句中我們分別提取了名字和姓氏。
這將會成功地把字符串“Francesco”綁定到變量Name上並把字符串“Cesarini”綁定到變量Surname上。
11> {Element, Element, _} = {1,1,2}.
{1,1,2}
就如使用變量一樣,在一個模式中我們也可以使用通配符,_,。這將匹配任何東西,並且不產生任何綁定。
我們剛纔看到了變量可以用下劃線開始,下劃線是一個特殊的標記,它告訴編譯器,這些變量是無關緊要的,它們只是作爲程序中不需要的變量的佔位符。
“無關緊要的”變量的功能跟普通變量一樣,可以檢查、使用和比較它們的值。唯一不同的是,普通變量的值如果從未使用過,編譯器將會發出警告,而使用“無關緊要的”變量則不會。使用“無關緊要的”變量是一種很好的編程實踐,這告訴閱讀代碼的人應該忽略掉這個值。
爲了提高可讀性和維護性,程序員經常以“無關緊要的”變量的形式引入值和類型。
下劃線本身也是個“無關緊要的”變量,但不能訪問其內容:因爲它的值被忽略了而且從未綁定。
11.函數
Erlang程序包含相互調用的函數。函數組合在一起並在模塊中定義。函數的名稱是一個基元。
一個函數的頭包括名字,隨後是一對括號,在裏面包含多個形式的參數或者沒有參數。
在Erlang中,函數參數的數量叫做元數。使用箭頭(->)來分隔函數頭和函數主體。
在這兩種操作系統下,你可以在Erlang終端中使用cd(Directory)進入到各個目錄。
一旦進入該目錄,你就可以在Erlang的終端中使用c(Module)並省略名稱中的.erl後綴來進行編譯。
如果代碼中沒有錯誤,編譯就會成功。
factorial(0) -> 1;
factorial(N) ->
N * factorial(N-1).
正如我們前面提到過的,模式匹配發生在函數的頭部,如何匹配成功,就會綁定變量N的一個實例。對每個語句來講變量都是本地的,它們不需要分配或釋放,Erlang運行時系統會自動處理這些。(方法定義的時候,前面先用;來表示,最後一個方法用.來表示)
12.模塊:
函數組合在一起構成了模塊。一個程序往往是分散在幾個模塊當中,每個模塊包括按邏輯組合在一起的函數。
模塊文件以.erl後綴來結尾,文件名稱和模塊名稱必須是相同的。
若要執行從一個模塊中導出的函數,你必須編譯代碼,這會在與模塊相同的目錄下面生成一個module.beam文件:
模塊可以這樣直接命名–module(Name),因此在例2-2中,demo模塊將存儲在一個名爲demo.erl的文件中。
-module(demo).
-export([double/1]).
% This is a comment.
% Everything on a line after % is ignored.
double(Value) ->
times(Value, 2).
times(X, Y) ->
X*Y.
export指令以Function/Arity的格式(方法名/參數個數),包含了導出函數的一個列表。這些函數是全局性的,這意味着可以從模塊的外部調用它們。
在Erlang中,註釋以百分號(%)開始直到該行結束。請務必在代碼中多多使用它們!
在Windows環境下,一種打開werl終端的方法是右擊一個.beam文件,然後從彈出的窗口菜單中選擇Open With option的“werl”選項。從現在起,雙擊任何源文件相同目錄下的.beam文件,就會打開一個Erlang終端了。
在這兩種操作系統下,你可以在Erlang終端中使用cd(Directory)進入到各個目錄。一旦進入該目錄,你就可以在Erlang的終端中使用c(Module)並省略名稱中的.erl後綴來進行編譯。如果代碼中沒有錯誤,編譯就會成功。
1> cd("/home/francesco/examples").
/home/francesco/examples
ok
2> c(demo).
{ok,demo}
3> demo:double(10).
20
4> demo:times(1,2).
** exception error: undefined function demo:times/2
13.從複合數據類型中提取值
Person = {person, "Mike", "Williams", [1,2,3,4]}.
{person, Name, Surname, Phone} = Person.
Name.
小寫字母開頭表示基元,大寫字母開頭表示變量
14.case
防禦性編程
假設程序把一個整數映射到一個表示一週中某天的基元。使用catch-all語句的防錯性編程如下所示。
convert(Day) ->
case Day of
monday -> 1;
tuesday -> 2;
wednesday -> 3;
thursday -> 4;
friday -> 5;
saturday -> 6;
sunday -> 7;
Other -> {error, unknown_day}
end.
listlen(Y) ->
case Y of
[] -> 0;
[_|Xs] -> 1 + listlen(Xs)
end.
if case X rem 2 of
X rem 2 == 1 -> odd; 1 -> odd;
X rem 2 == 0 -> even 0 -> even
end end
15.保護元
保護元(guard)是一個額外的限制條件,它應用於函數的case或者receive語句中(我們會在第4章中具體講述receive表達式)。
保護元應該放在“->”之前來分隔語句的主體和頭部。
保護元由when關鍵字和緊跟其後的一個保護元表達式組成。只有在模式匹配和保護元表達式求值結果爲基元true的情況下,這個語句纔會執行。
max(X,Y) when X>Y- > X;
max(X,Y) - > Y.
接下來重寫第2章中階乘的例子:
factorial(0) -> 1;
factorial(N) ->
N * factorial(N-1).
這一次使用保護元:
factorial(N) when N > 0 ->
N * factorial(N - 1);
factorial(0) -> 1.
my_add(X,Y) when not(((X>Y) or not(is_atom(X))
) and (is_atom(Y) or (X==3.4))) ->
X+Y.
新的保護元函數是is_atom/1和is_integer/1等。
元編程:
一個函數在運行時才確定將調用哪些函數的特性叫做元編程,也就是程序創建程序並運行。
16.內置函數:
要從標準輸入讀取一行,請使用io:get_line/1,它需要提示符字符串(或基元)作爲它的輸入:
1> io:get_line("gissa line>").
gissa line>lkdsjfljasdkjflkajsdf.
"lkdsjfljasdkjflkajsdf.\n"
也可以這樣讀取指定數量的字符:
2> io:get_chars("tell me> ",2).
tell me> er
"er"
17.
keysearch(Key, N, TupleList) -> {value, Tuple} | false
這個函數和keyfind差不多,就是返回值的結構不一樣
也是在TupleList中找一個Tuple,這個Tuple的第N個元素和Key一樣。
例子:
List1 = [{name,"zhangjing"},{name,"zhangsan"}]
lists:keysearch("zhangjing",2,List1).
結果:
{value,{name,"zhangjing"}}
lists:keysearch(1,1,[{1,2},{2,4}]).
第一個元素的值和key值一樣。
18.匹配模式,函數匹配模式
Page40 模塊是Erlang中代碼的基本單元,我們編寫的所有函數都存於模塊之中。模塊文件通常以.erl爲擴展名的文件中。
要運行一個模塊,首先需要編譯它,編譯成功之後的模塊文件其擴展名爲.beam
模塊名:方法名(). 來調用模塊裏面的方法
小寫字母是基元,大寫字母是變量
函數cost/1 :符號Name/N表示一個帶有N個參數名爲Name的函數,N稱爲函數的運算目。
函數cost/1必須從模塊之中導出,如果你想從模塊的外部調用它,這是必須的。即用-compile(export_all)來導出模塊之中的所有函數
[x]就是[x|[]]的縮寫
19.fun
P47。fun就是匿名函數,先定義一個Z,然後將fun賦值給一個變量Z (基元是小寫字母開頭的,就當做是一個常量,變量大寫字母開頭的,相當於一個變量)
1>Z=fun(X)-> 2*X end.
2>Z(2)
4
fun匹配多個函數 p48
20. 逗號(,)用來分隔函數調用、數據構造器以及模式中的參數
分號(;)用來分隔子句,在這幾種情況相下都會用到子句:分段的函數定義、case語句、if語句、try....catch語句以及receive表達式。
無論何時,我們只看到一組後面跟有表達式的模式,都會使用分號進行分隔。
div 整數除法
rem 整數取餘
21.返回fun的fun的值的時候,
Double=fun(x)->(2*x)end.
Double(5).
10
%定義兩層fun
Mult= fun(Time)->(fun(X)->(Time*X)) end.
調用它的時候, Trip=Mult(5). Time的值爲5
Trip(3). X的值爲3
(1.)Mult=fun(time,A,B)->A*B end.
調用 Mult(time,2,3) 返回6
(2.)Mult(A,B)-> A*B end.
調用 Mult(3,4) 返回12
求和:
sum(H|T)-> H+sum(T);
sum([])->0.
將此文件儲存爲mylists.erl
c(mylists.erl). %%先將此模塊編譯
然後再調用
L=[1,2,3]
mylists.sum(L).
6
map(_,[]) ->[];
map(F,[H|T]) ->[F(H)|map(F,T)]. %[F[H]|F[T]|[]] 將依次的列表元素當做參數傳進方法裏面去 (F變量代表的是一個方法名)
L=[1,2,3,4]
mylists.map(fun(x)->2*x end,L). %% fun(1)+fun(2)+fun(3)+fun(4)
解析:
[2,4,6,8]
注:不能將自己的模塊定義成lists ,因爲有系統的模塊名爲這個名字
模塊 -import(lists,[map/2,sum/1]).
如果有導入聲明的時候,不需要寫lists:map(...),可以直接使用map(....), 如果沒有導入的函數,記得要加上模塊名才能使用
23.
列表解析: (列表解析很有作用)
L=[1,2,3,4]
[2*X||X<-L]. %[F(X)||X<-L]代表"由F(X)組成的列表,其中X是取值於列表L"
使用列表解析實現過濾功能:
[X||{a,X}<-{a,1},{1,mn},{a,'m'}]
[1,'m']
使map方法更簡潔:
map(F,L)->[F(X)||X<-L]. %<-不是方法體,而是說X的值是從L裏面取的
列表解析很強大!!
list.seq(1,N)返回一個由1到N整數組成的列表,所以A<-lists:seq(1,N)意味着A的取值範圍是1到N的所有整數。
max(X,Y) ->
case X>Y of
true->X;
false->Y;
end.
max(X,Y) ->
if X>Y
true - >X;
false ->Y;
end.
運用保護元:
max(X,Y) when X>Y -> X;
max(X,Y) ->Y.
什麼情況下使用end結尾??
斷言序列 ==保護元 Page58
24.記錄
Page60.
-record(Name,{key1=Default1,
key2=Default2,
.....
}).
定義record
-record(todo,{status=remider,who=joe,text}
})
創建和更新記錄
X=#todo{}.
X1=#todo{status=urgent,text='Fix errata in book'}.
X2=X1#todo{status=done}.
#用於創建類型爲todo的新記錄,key都是原子。
X2=X1#todo{status=done}. 這個表示創建了一個X1(X1必須爲todo類型的)的副本,並將value的值改爲了done,原記錄本身並未改變
記錄的匹配模式:
#todo{who=W,text=Txt} =X2.
W.
joe
Txt.
Fix errata in book
%%匹配操作符的左邊,我們使用了自由變量W和Txt來定義一個記錄模式
如果只是想提取某個記錄之中的對應的值,X2#todo.text來取值
-module(shop).
-export(cost).
cost(oranges) ->5;
cost(apple) ->8;
cost(milk) ->16.
-module(shop1).
-export([total/1]).
total([{What,N}|T]) -> shop:cost(What)*N+total(T);
total([]) -> 0.
嘗試在Erlang終端輸入xd ,進入到相應的目錄
在這兩種操作系統下,你可以在Erlang終端中使用cd(Directory)進入到各個目錄。一旦進入該目錄,你就可以在Erlang的終端中使用c(Module)並省略名稱中的.erl後綴來進行編譯。如果代碼中沒有錯誤,編譯就會成功。
-module(demo).
-export([double/1]).
% This is a comment.
% Everything on a line after % is ignored.
double(Value) ->
times(Value, 2).
times(X, Y) ->
X*Y.
1> cd("/home/francesco/examples").
/home/francesco/examples
ok
2> c(demo).
{ok,demo}
3> demo:double(10).
20
4> demo:times(1,2).
** exception error: undefined function demo:times/2
25.順序型編程進階
二進制數據:在Erlang中可以使用一種二進制數據結構來存儲大量的原始數據。相對於列表或者元組,二進制類型更加節省內存,而且運行時系統也對此進行了優化。
在書寫和打印時,二進制數據以一個整數或者字符序列的形式了來出現,兩端分別用兩個小於號和兩個大於號括起來,例如:
<<5,10,20>>.
<<5,10,20>>
<<"hello">>.
<<"hello">>
列表構造:[1,2,[3,4,5],6|[8]] (記得測試下)
比特語法:
M=<<X:3,Y:6,Z:7>>.
atom_to_list([hello]).
"hello"
[2*X||X<-[1,2,3]]
fileName .hrl包含的文件。
-include("lerne/file.hrl").
短路布爾表達式:
Expr1 orelse Expr2 %%Expr1爲true的時候,不會對Expr2求值, 只有當Expr1爲false的時候,纔會對Expr2求值。
Expr1 andalso Expr2 %%Expr1爲true的時候,Expr2必須被求值, 只有當Expr1爲false的時候,纔不會對Expr2求值。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.