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求值。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章