热门问题
时间线
聊天
视角

Erlang

来自维基百科,自由的百科全书

Erlang
Remove ads

Erlang/ˈɜːrlæŋ/)是一種通用的平行導向函數式程式語言。Erlang也可以指Erlang/OTP的通稱,開源電信平台(OTP)是Erlang的常用執行環境及一系列標準元件。

快速預覽 編程範型, 設計者 ...
Remove ads
Thumb
LYME is Erlang-based

Erlang 執行環境為專有以下要求的系統設計:

Erlang是運作於虛擬機直譯式語言,但是現在也包含有烏普薩拉大學高效能Erlang計劃(HiPE)[3]開發的原生程式碼編譯器,自R11B-4版本開始,Erlang也支援腳本直譯器。在編程範型上,Erlang屬於多重典範程式語言,涵蓋函數式平行式分散式。循序執行的Erlang是一個及早求值, 單次賦值動態資料形態函數式程式語言

它由喬·阿姆斯特朗(Joe Armstrong)在瑞典電信裝置製造商愛立信所轄的電腦科學研究室開發,目的是創造一種可以應付大規模並發活動的程式語言執行環境。Erlang於1987年釋出正式版本,最早是愛立信擁有的私有軟件,經過十年的發展,於1998年發表開放原始碼版本。

Remove ads

開發及演變歷史

Erlang得名於丹麥數學家統計學家Agner Krarup Erlang,同時Erlang還可以表示Ericsson Language。Erlang語言由瑞典愛立信電訊公司的喬·阿姆斯特朗開始設計,開始於公元一九八零年代。最初是以Prolog程式語言為基礎,幾度改版之後,改成以Joe's Abstract Machine為基礎的獨立語言執行環境。雖然語言風格仍與Prolog相近,不過因Erlang語言設計的走向,Erlang成為具備函數語言特色的程式語言[4]

發行版本

1998年起,Erlang發布開放原始碼版本,稱為開源電信平台開源電信平台採用修改過的Mozilla公共許可證協定發放,同時愛立信仍然提供商業版本的技術支援。目前,Erlang最大的商業用戶是愛立信,其他知名用戶有北電網絡亞瑪遜以及T-Mobile[5]

語言特色

  • 平行導向程式設計 在語言中,可以藉由spawn/*函數,將特定的函數設置為獨立的行程,之後可以做跨行程通訊。
  • 函數式程式設計 由於Erlang早期以Prolog開發製成,受語言特性影響,即成為函數式語言。
  • 單次賦值 每個變數只能跟資料綁一次,所以,不像一般程式語言的變數可以多次指定為不同的值。單次賦值的好處是狀態單純,使程式容易閱讀。
  • 及早求值或嚴格求值 Erlang基本求值策略為電腦語言中及早求值之特性。而且,可以藉由明確使用無參數的λ演算式,將特定函數設置為惰性求值策略。
  • 動態資料型態與類型系統 有編譯時期的型態檢查系統支援。
  • 錯誤先發 在執行時期發生的錯誤,會由錯誤位置送出訊息,發生錯誤的行程立刻停止動作。藉由行程鏈通機制,可以自動傳遞錯誤、捕捉錯誤,使其他行程能夠幫助處理錯誤。
  • 程式碼熱更新 由於Erlang是函數語言,可以撰寫特定的程式結構,製作即時更換新版函數的機制。
  • 手稿語言 Erlang實作提供了指令碼執行方式。

語言構成

Erlang程式結構以函數定義為主。函數是一組將輸入分別對應到輸出的規則,對應方式遵守數學函數的慣例。此外,Erlang語言由幾項構句要素所組成,包括文字(或稱原子)、數字、列表、值組、字元、字串、二進位資料、模組、與特定用途的關鍵字如fun ... end, if ... end, case ... of ... end, spawn, !, receive ... end等等。以下段落分別列示並舉例說明Erlang程式的基本構成部份,涵蓋資料格式表達式格式內建函數

資料格式

更多資訊 類型, 意義與構詞規則 ...
Remove ads

表達式格式

更多資訊 類型, 構詞規則 ...
Remove ads

內建函數

開源電信平台包括一個Erlang直譯器、一個Erlang編譯器、程式節點通訊協定、CORBA、一個分散式資料庫Mnesia頁面存檔備份,存於互聯網檔案館)、以及許多程式庫[6]內建函數涵蓋了各種方面的功能,涵蓋了系統命令、資料存取、格式轉換、網絡通訊、圖形介面、 ... 等。以下列表介紹幾項常用的Erlang內建函數。(參閱檔案頁面存檔備份,存於互聯網檔案館)或索引頁面存檔備份,存於互聯網檔案館))

更多資訊 模組:函數名稱 / 參數數目, 用途 ...
Remove ads

Hello World 程式

這是輸出 Hello World 的一種方式:[7]

-module(hello).
-export([hello_world/0]).

hello_world() -> io:fwrite("hello, world\n").

若要編譯這個程式,將它存為一個名為 hello.erl 的文字檔,然後從 Erlang終端 進行編譯。不要忘了在每個命令的最後加上一個句號(.)。例如:

Erlang (BEAM) emulator version 4.9.1 [source]
Eshell V4.9.1  (abort with ^G)
1> c(hello).
{ok,hello}

(在 Unix系統 上,你可以通過在命令列裏輸入 "erl" 來進入 Erlang終端。在 Windows系統 上,你需要打開一個 命令提示符 視窗,然後輸入 "werl"來進入 Erlang終端,或者在程式功能表中找到 Erlang 的圖示。)從 Erlang終端 上運行這個程式:

2> hello:hello_world().
hello, world
ok
Remove ads

函數式程式設計

Erlang支援函數式程式設計的一般特色,特色包括單次賦值遞歸定義、λ演算高階函數等等。Erlang函數大致寫法如下,以整數階乘模組為例:

-module(fact).
-export([fac/1]).

fac(N) when N > 1 ->
    N * fac(N-1);
fac(1) ->
    1.

以下是快速排序演算法的Erlang實作:

%% quicksort:qsort(List)
%% Sort a list of items
-module(quicksort).
-export([qsort/1]).

qsort([]) -> [];
qsort([Pivot|Rest]) ->
    qsort([ X || X <- Rest, X =< Pivot]) ++ [Pivot] ++ qsort([ Y || Y <- Rest, Y > Pivot]).

以下是費氏數列求解函數:

-module(example).
-export([fibo/1]).

fibo(N) when N > 1 ->
    fibo(N-1) + fibo(N-2);
fibo(1) ->
    1;
fibo(0) ->
    0.
> c(example).
{ok,example}
> lists:map(fun(X)->example:fibo(X) end, lists:seq(1,10)).
[1,1,2,3,5,8,13,21,34,55]

函數式程式設計難免以遞歸計算,而消耗了大量遞歸堆疊空間。為了克服這個問題,一般使用累積參數尾端遞迴等技巧節省遞歸數目:如以下例子。

-module(test).
-export([fibo_accu/1]).

fibo_accu(N) ->
    fibo(N, 0, 1).
fibo(N, C1, C2) when N > 2 ->
    fibo(N-1, C2, C1+C2);
fibo(0, _, _) ->
    0;
fibo(1, _, _) ->
    1;
fibo(_, C1, C2) ->
    C1+C2.
> c(example).
{ok,test}
> lists:map(fun(X)->test:fibo_accu(X) end, lists:seq(1,10)).
[1,1,2,3,5,8,13,21,34,55]

函數式程式設計容許使用高階函數求解。以下例子說明Erlang實做複合函數。 ( f o g ,唸作 f after g 。)

'After'(F, G) ->
     fun(X) ->
         erlang:apply(F, [erlang:apply(G, [X])])
     end.
  • 請注意after是Erlang關鍵字。因此,以上函數命名為′After′避開關鍵字。
> (example:'After'(fun test:show/1, fun test:parse/1))(3.1416).
Real number 3.141600 is met.
ok

平行式程式設計

Erlang最主要的特色是平行導向程式設計,強調多程式平行運作,並且以訊息對彼此溝通[8]。Erlang提供了spawn函數和 !receive ... end 等關鍵字,可以描述在Erlang/開源電信平台中的如何啟動一些程式、並且如何讓程式傳遞訊息。此外,平行導向程式設計的精神還強調程式的容錯處理,藉由程式發生錯誤時的訊息傳遞,使其他程式可以得知錯誤的發生,使方便於後續處理。以下分別介紹平行導向程式設計的一般程式撰寫方式,以及錯誤處理的使用方式。

平行導向程式設計

基本的平行程式示範如下:

  • 以下啟動一個程式。
% create process and call the function web:start_server(Port, MaxConnections)
ServerProcess = spawn(web, start_server, [Port, MaxConnections]),
  • 以下是在任何程式中,對先前起動的程式送一則訊息 {pause, 10} 。
% send the {pause, 10} message (a tuple with an atom "pause" and a number "10")
% to ServerProcess (asynchronously)
ServerProcess ! {pause, 10},

  • 以下是一段接收訊息的程式。每個程式都擁有一份郵箱,可佇留收到的訊息; receive ... end 程式片斷是從程式的郵箱中取出最早佇留的訊息。
% receive messages sent to this process
receive       
        a_message -> do_something; 
        {data, DataContent} -> handle(DataContent);
        {hello, Text} -> io:format("Got hello message: ~s", [Text]);
        {goodbye, Text} -> io:format("Got goodbye message: ~s", [Text])
end.

收到 a_message 結果就是 do_something ;收到 {data, DataContent} 結果會呼叫 handle(DataContent) ;
收到 {hello, Text} 結果教是印出 "Got hello message: ..." ,收到 {goodbye, Text} 結果是印出
"Got goodbye message: ..." 。

以下程式,示範產生一組環狀傳遞訊息的程式。

ring_proc(Funs) ->
    Ns = lists:seq(1, length(Funs)),
    [P|Pids] = [ spawn(?MODULE, lists:nth(Nth,Funs),[]) || Nth <- Ns ],
    [ Pid ! ToPid || {Pid, ToPid} <- lists:zip([P|Pids], Pids++[P]) ]. 

func() ->
    receive
	ToPid ->
	    func_msg_(ToPid)
    end.

func_msg_(ToPid) ->
    receive
	stop ->
	    io:format("Stop process ~w~n", [self()]),
	    ToPid ! stop;
	Message ->
	    io:format("~w: transmit message to ~w~n", [self(), ToPid]),
	    ToPid ! Message,
	    func_msg_(ToPid)
    end.

接收stop訊息,就對下一個程序送stop訊息;接收到其他任何訊息,就對下一個程序送同樣的訊息。

如果傳送任何其他訊息,就會讓所有的程式不斷對下一個程式傳遞訊息。而以下是測試傳送stop訊息的執行結果。

> [P|_] = example:ring_proc([func,func,func]).
[<0.233.0>,<0.234.0>,<0.232.0>]
> P ! stop.
Stop process <0.233.0>
stop
Stop process <0.234.0>
> Stop process <0.232.0>
>
Remove ads

容錯處理

Erlang容錯處理機制,由二個步驟實現:一是將二個程式連接起來,二者之間存在一道通訊管道,可提供錯誤訊息的傳遞 ── 在此使用link/1函數;二是將程式回報錯誤的機制打開 ── 在此使用process_flag/2函數。

  • 使用link(Pid)讓程式連接到另一個程式。
-module(example).
-compile(export_all).
hello() ->
    Pid = spawn(?MODULE, world, []),
    link(Pid),
    ... .

執行時,以 Pid = spawn(example, hello, []) 啟動程序,此程序將啟動另一個程序,並且與它連接。
但以上程式還不會有錯誤訊息的傳遞機制,因為回報錯誤的開關還沒有打開。
  • 開啟程式回報錯誤機制。
以上 hello/0 函數前段使用process_flag/2函數,將trap_exit標籤打開,即可開啟程序回報錯誤機制。
hello() ->
    process_flag(trap_exit, true),
    Pid = spawn(?MODULE, world, []),
    link(Pid),
    ... .

於是,當程式結束時,會送出{'EXIT', From, Reason}資料。程式正常結束時,Reasonnormal

另外,spawn函數另外有程式連接版本,spawn_link函數,同時啟動並連接到新程式。

分散式程式設計

Erlang提供分散式機制,能在另一台電腦啟動一些Erlang程式,並由本機電腦對其他電腦的Erlang程式傳遞訊息。

  • 當啟動Erlang環境時,加上一個網絡節點名稱,就進入分散式Erlang模式。節點可以使用埠號與其他節點通訊。
$> erl -name node_1
  • 在同一個網域中,網絡節點名稱可以使用短名。
$> erl -sname node_1

啟動新的網絡節點時,Erlang使用epmd (Erlang埠號對應管理系統) 指派埠號,提供節點使用。

當知道一個網絡節點名稱時,可以在該節點產生新程式。

  • 在指定節點RemoteNode啟動一個程式,spawn啟動參數依序為節點名稱、模組名稱、函數名稱、函數的參數列。
% create a remote process and call the function web:start_server(Port, MaxConnections)
% on machine RemoteNode
RemoteProcess = spawn(RemoteNode, web, start_server, [Port, MaxConnections]),

在遠端節點產生新程序之後,可以使用平行式程式設計的技巧,與遠端程序通訊。

Erlang / 開源電信平台提供的程式庫,於分散式程式設計可以使用net_admnet_kernelslave、... 等模組,做網絡通訊[9]

其他程式設計典範

惰性求值

Erlang程式員可以使用惰性求值。不過,必須使用λ演算式,才能做到惰性求值。

以下是惰性求值的一例:假設有個剖析器程式如下,由於及早求值特徵,本程式將不會求解。
expr() -> alt(then(factor(), then(literal($+), factor())),
              then(factor(), then(literal($-), factor()))).
factor() -> alt(then(term(), then(literal($*), term())),
                then(term(), then(literal($/), term()))).
term() -> alt(number(),
              xthen(literal($(), thenx(expr(), literal($))))).
此處使用λ演算式及適當使用函數名稱表示,就能進行求值。示例如下。
expr() ->
    fun () ->
          alt(then(fun factor/0, then(literal($+), fun factor/0)),
              then(fun factor/0, then(literal($-), fun factor/0)))
    end.
factor() ->
    fun () ->
            alt(then(fun term/0, then(literal($*), fun term/0)),
                then(fun term/0, then(literal($/), fun term/0)))
    end.
term() ->
    fun () ->
          alt(number(),
              xthen(literal($(), thenx(expr(), literal($)))))
    end.

應用


社區

參考資料

延伸閱讀

外部連結

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads