閱讀369 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Erlang入門(一)

  讀erlang.org上麵的Erlang Course四天教程
1.數字類型,需要注意兩點
1)B#Val表示以B進製存儲的數字Val,比如
7> 2#101.
5
進製存儲的101就是10進製的5了
2)$Char表示字符Char的ascii編碼,比如$A表示65

2.比較難以翻譯的概念——atom,可以理解成常量,它可以包含任何字符,以小寫字母開頭,如果不是以小寫字母開頭或者是字母之外的符號,需要用單引號包括起來,比如abc,'AB'

3.另一個概念——Tuple,有人翻譯成元組,可以理解成定長數組,是Erlang的基礎數據結構之一:
8> {1,2,3,4,5}.
{
1,2,3,4,5}
9> {a,b,c,1,2}.
{a
,b,c,1,2}
10> size({1,2,3,a,b,c}).
6
內置函數size求長度,元組可以嵌套元組或者其他結構。下麵所講的列表也一樣。

4.另外一個基礎數據結構就是各個語言都有的list(列表),在[]內以,隔開,可以動態改變大小,
    [123, xyz]
    [
123, def, abc]
    [{person
, 'Joe', 'Armstrong'},
        {person
, 'Robert', 'Virding'},
        {person
, 'Mike', 'Williams'}
    ]
可以使用內置函數length求列表大小。以""包含的ascii字母代表一個列表,裏麵的元素就是這些字母的ascii值,比如"abc"表示列表[97,98,99]。

5.通過這兩個數據結構可以組合成各種複雜結構,與Lisp的cons、list演化出各種結構一樣的奇妙,Erlang也可以當作是操作列表的語言。

6.Erlang中變量有兩個特點:
1)變量必須以大寫字母或者下劃線開頭,可以包含字母、下劃線和@
2)變量隻能綁定一次,也就是所謂的Single Assignment。或者以一般的說法就是隻能賦值一次,其實Erlang並沒有賦值這樣的概念,=號也是用於驗證匹配。

7.模式匹配——Pattern Matching,Erlang的模式匹配非常強大,看了buaawhl的《Erlang語法提要》的介紹,模式匹配的功能不僅僅在課程中介紹的數據結構的拆解,在程序的分派也扮演重要角色,或者說Erlang的控製的流轉是通過模式匹配來實現的。具體功能參見鏈接,給出書中拆解列表的例子:
    [A,B|C] = [1,2,3,4,5,6,7]
        Succeeds 
- binds A = 1, B = 2,
        C 
= [3,4,5,6,7]
    
    [H
|T] = [1,2,3,4]
        Succeeds 
- binds H = 1, T = [2,3,4]
    
    [H
|T] = [abc]
        Succeeds 
- binds H = abc, T = []
    
    [H
|T] = []
        Fails

下麵會給出更多模式匹配的例子,給出一個模塊用來計算列表等

8.Erlang中函數的定義必須在一個模塊內(Module),並且模塊和函數的名稱都必須是atom,函數的參數可以是任何的Erlang類型或者數據結構,函數要被調用需要從模塊中導出,函數調用的形式類似:
moduleName:funcName(Arg1,Arg2,...).
寫我們的第一個Erlang程序,人見人愛的Hello World:
-module(helloWorld).
-export([run/1]).
run(Name)
->
    io
:format("Hello World ~w~n",[Name]).
存為helloWorld.erl,在Erlang Shell中執行:
2> c(helloWorld).
{ok
,helloWorld}
3> helloWorld:run(dennis).
Hello World dennis
ok
打印出來了,現在解釋下程序構造,
-module(helloWorld).
這一行聲明了模塊helloWorld,函數必須定義在模塊內,並且模塊名稱必須與源文件名相同。
-export([run/1]).
而這一行聲明導出的函數,run/1指的是有一個參數的run函數,因為Erlang允許定義同名的有不同參數的多個函數,通過指定/1來說明要導出的是哪個函數。
接下來就是函數定義了:
run(Name)->
    io
:format("Hello World ~w~n",[Name]).
大寫開頭的是變量Name,調用io模塊的format方法輸出,~w可以理解成占位符,將被實際Name取代,~n就是換行了。注意,函數定義完了要以句號.結束。然後執行c(helloWorld).編譯源代碼,執行:
helloWorld:run(dennis);

9.內置的常用函數:
    date()
    
time()
    
length([1,2,3,4,5])
    size({a
,b,c})
    atom_to_list(an_atom)
    list_to_tuple([
1,2,3,4])
    integer_to_list(
2234)
    tuple_to_list({})
    hd([1,2,3,4])  %輸出1,也就是列表的head,類似Lisp的car
    tl([1,2,3,4])  %輸出[2,3,4],也就是列表的tail,類似List的cdr

10.常見Shell命令:
1)h(). 用來打印最近的20條曆史命令
2)b(). 查看所有綁定的變量
3) f(). 取消(遺忘)所有綁定的變量。
4) f(Val).  取消指定的綁定變量
5) e(n).   執行第n條曆史命令
6) e(-1).  執行上一條shell命令

11.又一個不知道怎麼翻譯的概念——Guard。翻譯成約束?嗬嗬。用於限製變量的類型和範圍,比如:
     number(X)    - X 是數字
    integer(X)    
- X 是整數
    float(X)    
- X 是浮點數
    atom(X)        
- X 是一個atom
    tuple(X)    
- X 是一個元組
    list(X)        
- X 是一個列表
    
    
length(X) == 3    - X 是一個長度為3的列表
    size(X) 
== 2    - X 是一個長度為2的元組
    
    X 
> Y + Z    - X >Y+Z
    X 
== Y        - X 與Y相等
    X 
=:= Y        - X 全等於Y
    (比如: 
1 == 1.0 成功
               
1 =:= 1.0 失敗)
為了方便比較,Erlang規定如下的比較順序:
number < atom < reference < port < pid < tuple < list
其中pid就是process的id。

12.忘了介紹apply函數,這個函數對於熟悉javascript的人來說很親切,javascript實現mixin就得靠它,它的調用方式如下:
apply(Mod, Func, Args),三個參數分別是模塊、函數以及參數列表,比如調用我們的第一個Erlang程序:
apply(helloWorld,run,[dennis]).
13.if和case語句,if語句的結構如下:
if
Guard1 ->
Sequence1 ;
Guard2 ->
Sequence2 ;
...
end
而case語句的結構如下:
case Expr of
Pattern1 [when Guard1] 
-> Seq1;
Pattern2 [when Guard2] 
-> Seq2;
dot.gif
PatternN [when GuardN] 
-> SeqN
end


if和case語句都有一個問題,就是當沒有模式匹配或者Grard都是false的時候會導致error,這個問題case可以增加一個類似java中default的:
case Fn of
dot.gif
   _ 
->
   true
end
通過_指代任意的Expr,返回true,而if可以這樣:
if
  
dot.gif
  true 
->
   true
end
一樣的道理。case語句另一個需要注意的問題就是變量範圍,每個case分支中定義的變量都將默認導出case語句,也就是在case語句結束後可以被引用,因此一個規則就是每個case分支定義的變量應該一致,不然算是非法的,編譯器會給出警告,比如:
f(X) ->
case g(X) of
true 
-> A = h(X), B = A + 7;
false 
-> B = 6
end
,
h(A)
.

如果執行true分支,變量A和變量B都被定義,而如果執行的false分支,隻有變量B被定義,可在case語句執行後,h(A)調用了變量A,這是不安全的,因為變量A完全可能沒有被定義,編譯器將給出警告
variable 'A' unsafe in 'case' (line 10)



14.給出一些稍微複雜的模型匹配例子,比如用於計算數字列表的和、平均值、長度、查找某元素是否在列表中,我們把這個模塊定義為list:
-module(list).
-export([average/1,sum/1,len/1,double/1,member/2]).
average(X)
->sum(X)/len(X).
sum([H
|T]) when number(H)->H+sum(T);
sum([])
->0.
len([_
|T])->1+len(T);
len([])
->0.
double([H
|T]) -> [2*H|double(T)];
double([]) 
-> [].
member(H
, [H|_]) -> true;
member(H
, [_|T]) -> member(H, T);
member(_
, []) -> false.
                

細細體會,利用遞歸來實現,比較有趣,這其實與Lisp中利用尾遞歸來實現迭代是一樣的道理,[H|T]的形式類似Lisp中的car、cdr操作。_用於指代任意的變量,當我們隻關注此處有變量,但並不關心變量的值的時候使用。用分號;來說明是同一個函數定義,隻是不同的定義分支,通過模式匹配來決定調用哪個函數定義分支。
另一個例子,計算各種圖形的麵積,也是課程中給出的例子:

-module(mathStuff).
-export([factorial/1,area/1]).
factorial(
0)->1;
factorial(N) when N
>0->N*factorial(N-1).
%計算正方形麵積,參數元組的第一個匹配square    
area({square
, Side}) ->
    Side 
* Side;
%計算圓的麵積,匹配circle  
area({circle
, Radius}) ->
   
% almost :-)
   
3 * Radius * Radius;
%計算三角形的麵積,利用海倫公式,匹配triangle 
area({triangle
, A, B, C}) ->
   S 
= (A + B + C)/2,
math
:sqrt(S*(S-A)*(S-B)*(S-C));
%其他
area(Other) 
->
   {invalid_object
, Other}.

執行一下看看:
1> c(mathStuff).
{ok
,mathStuff}
2> mathStuff:area({square,2}).
4
3> mathStuff:area({circle,2}).
12
4> mathStuff:area({triangle,2,3,4}).
2.90474
5> mathStuff:area({other,2,3,4}).
{invalid_object
,{other,2,3,4}}


Erlang使用%開始單行注釋。

文章轉自莊周夢蝶  ,原文發布時間5.17

最後更新:2017-05-17 14:01:52

  上一篇:go  Erlang簡史(翻譯)
  下一篇:go  sicp習題2.2節嚐試解答