Erlang第三課 ---- 創建和使用module


----------------小技巧-----------------------------

因為這一課開始,我們要使用Erlang文件操作,所以,我們期待啟動shell的時候,當前目錄最好是是我們的工作目錄,比如,打開erl shell環境,默認目錄就是“C:/Users/Kenneth/Documents/Erlang”。我們需要編輯C:/Program Files/erl5.9.1/usr(啟動shell后,執行pwd().來查看當前目錄是是什么)下的.erlang文件來達到這一目的。windows下創建.erlang是件頭大的事,圖形界面操作時,系統提示“必須要輸入文件名”,可以先創建一個文本文檔,然后在cmd環境下執行move xxx .erlang來改文件名。

.erlang文件內容如下:

io:format("according to the file .erlang in ~p~n",
            [element(2, file:get_cwd())]).
%% Edit to the directory where you store your code
io:format("cd ").
c:cd("C:/Users/Kenneth/Documents/Erlang").
io:format("...~n").
io:format("OK! Now in:~p~n", [element(2, file:get_cwd())]).

這樣下次啟動shell,它會自動切換當前目錄為我們的工作目錄。

---------------------------------------------------

Erlang里面會把某一類的function放到一個單獨的文件中,然后這個整體被叫做Module;Erlang中所有的函數都必須放到Module里面。所以,除了前面兩課講到的基本數據類型,Module也是Erlang的基本元素之一。

 

1、調用函數時,我們必須以這樣的結構:Module:Function(Arguments)。除了BIFs。

 

2、BIF(Build In Function)使用時不需要指定module,這是因為虛擬機啟動時,默認加載了Erlang這一module里的所有函數進來。所有的arithmetic,logic,和boolean operator這些,也都是在Erlang 這一module中。例如:

> erlang:element(2,{a,b,c}).
b
> element(2,{a,b,c}).
b
> lists:seq(1,4).
[1,2,3,4]
> seq(1,4).
** exception error: undefined shell command seq/2

上面的代碼中,不管我們指不指明element函數屬於Erlang模塊,它都是可以被執行的;另外一面,seq屬於lists模塊,所以,如果不指定它屬於哪個module,系統就會拋出錯誤。

ps:不是所有Erlang中所有的函數都被加載,有一些不常用的,是沒有加載的。

 

3、除了需要一個單獨的文件,並且給他起一個別致的名字以外,Functions和Attributes這兩樣也是一個Module不可缺少的。Function不必多講,沒有Function,建立Module做什么?OK,那Attributes又是做什么用的?Attributes里面存放了描述Module自身用的一些metadata,比如它自己的名字,哪些函數是外部可以調用的,代碼作者啊等等這些。編譯器會將這些信息放到編譯完的代碼中去。這樣,即使是拿到編譯過的代碼,我們也可以查看這個module的基本信息,而不用再去翻source code。

attributes的定義方法是 -Name(Attribute)。

 

4、第一個Attributes必須是“-module(Name)”;這里的Name還必須和存放Module的file同名。這里的name便是我們按照MF(A)這樣調用函數時,必須要用到的module name。

 

5、然后另一個必不可少的Attributes是" -export([Function1/Arity, Function2/Arity,...,Function3/Arity])"。其中,Function是函數名稱,Arity是這個函數接受幾個參數。在Erlang中,相同名稱不同參數個數的函數是當做不同函數對待的,這類似OOP里面的重載。

 

6、還有一個attributes比較重要“-import(Module, [Function/Arity, ..., Function/Arity])”,其作用就是將module中的函數加載進來,然后,我們調用的時候就直接F(A)即可(就像虛擬機一開始對Erlang這一module做的一樣)。這里有個問題,很多人都不大贊成用import來完成工作。主要因為,本來在設計module的時候,只需要考慮function name在本module內唯一就好了,但是,現在如果從不同的module引入function,使用的時候,就得小心翼翼了。

 

7、define是另一個比較重要的attributes,使用方法是“-define(MACRO, some_value)”。這個和C中的define有點像。同樣的define也是常被用來定義一些常量和短的函數。Erlang的宏定義使用的時候,格式為“?MACRO”。

ps:?MODULE會被換成module name,as a atom;?FILE會被換成本文件的filename,as a string;?LINE會被換做所在行的行號。

ps:同樣的我們可以使用-ifdef(MACRO),-else和-endif等宏定義檢查語句,比如:

-ifdef(DEBUGMODE).
-define(DEBUG(S), io:format("dbg: "++S)).
-else.
-define(DEBUG(S), ok).
-endif.

如此,我們可以在后面的代碼中使用?DEBUG("entering xxx function!~n")這樣的語句來添加調試信息。這樣,如果我們如果在編譯的時候使用了DEBUGMODE開關,那么,調試信息將被打印出來;如果我們沒有使用DEBUGMODE開關,這句只是被替換為ok這一atom,單獨這樣一條語句的話,是什么也不會做。

 

8、函數的定義語法是Name(Args) -> Body(結束符)。Name必須是一個atom。Body可以是一條或者多條由逗號隔開的Erlang語句。(結束符)則是分號或者句號之一,我們在定義多個同名的函數時,每個函數之間使用“;”號隔開的,而所有函數定義實現都已經完成了之后,我們要在最后加一個“.”。

 

9、另外還有一個比較常見的attributes是:

-spec Module:Function(ArgType1, ..., ArgTypeN) -> ReturnType."

這里,如果是同一module內,我們可以把“Module”字段略去不寫。

這一語句是用來定義函數規范的,如果我們定義了函數規范,那么我們就可以在運行前使用dialyzer來對程序進行靜態檢查,這樣就可以發現一些我們由於手抖造成的錯誤。

下面這句,意思是,我們定義了一個函數index(),它需要三個參數,第一個參數可以是任意的數據類型,第二個是正整數,第三個參數是一個list(這里的[any()]和lists()是等價的),而return的是一個非負數。

-spec( index(any(), pos_integer(), [any()]) -> non_neg_integer() ).

另外與spec相關的還有type,具體可以戳下面:

http://erlangdisplay.iteye.com/blog/404570

 

================分割線===========================

so,一個module就這樣建好了,一起來看看我們建的第一個module:

-module(helloworld).
-export([greet_and_add_two/1]).

add(A,B) ->
    A + B.
    
%%% Shows greeting.
%%% io:format/1 is the standard function used to output text.

hello() ->
    io:format("Hi Erlang!~n").
    
greet_and_add_two(X) ->
    hello(),
    add(X,2).

 然后我們嘗試編譯並使用我們module:

> cd("C:/Users/zhenxingluo/Documents/Erlang").
C:/Users/zhenxingluo/Documents/Erlang
ok
> c(helloworld).
{ok,helloworld}  
> helloworld:greet_and_add_two(3).
Hi Erlang!
5
> helloworld:module_info().
[{exports,[{greet_and_add_two,1},
           {module_info,0},
           {module_info,1}]},
 {imports,[]},
 {attributes,[{vsn,[251566447801502640269456451546098169641]}]},
 {compile,[{options,[]},
           {version,"4.8.1"},
           {time,{2014,4,2,7,22,18}},
           {source,"c:/Users/zhenxingluo/Documents/Erlang/helloworld.erl"}]}]

這里的cd是Erlang shell專用的一個函數,就是change directory。然后,我們調用Erlang shell中的編譯函數c()。如果編譯沒有報錯,我們就會看到helloworld.erl文件的旁邊,有生成一個文件叫helloworld.beam。

事實上,beam代表Bogdan/Bjorn's Erlang Abstract Machine;這個VM只是眾多Erlang的虛擬機之一,但是好像其他都不怎么用了。恩,繼續編譯沒有報錯的話,因為我們當前目錄是helloworld.beam所在目錄,所以,VM是看得見這個文件的。那么,我們就可以在shell窗口里直接調用他了。就像我們看到的那樣。

這里的module_info()是編譯器加入的一個函數,調用此函數可以查看我們module的metadata。同時,它也支持我們使用module_info/1,來查看其中的一項,比如,helloworld:module_info(attributes).


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM