Erlang
✍ dations ◷ 2024-12-24 02:51:54 #程序设计语言,并发编程语言,函数式编程语言,宣告式编程语言,Erlang,爱立信,1986年创建的编程语言
Erlang(/ˈɜːrlæŋ/)是一种通用的并发程序设计语言,它由乔·阿姆斯特朗(Joe Armstrong)在瑞典电信设备制造商爱立信所辖的计算机科学研究室开发,目的是创造一种可以应付大规模并发活动的程序设计语言和运行环境。Erlang于1987年发布正式版本,最早是爱立信拥有的私有软件,经过十年的发展,于1998年发表开放源码版本。
Erlang是运作于虚拟机的解释型语言,但是现在也包含有乌普萨拉大学高性能Erlang计划(HiPE)开发的原生代码编译器,自R11B-4版本开始,Erlang也支持脚本方式执行。在编程范型上,Erlang属于多重典范编程语言,涵盖函数式、并行及分布式。循序运行的Erlang是一个及早求值, 单次赋值和动态类型的函数式编程语言。
Erlang得名于丹麦数学家及统计学家Agner Krarup ,同时Erlang还可以表示icsson uage。Erlang语言由瑞典爱立信电信公司的乔·阿姆斯特朗开始设计,开始于公元一九八零年代。最初是以Prolog程序设计语言为基础,几度改版之后,改成以Joe's Abstract Machine为基础的独立语言运行环境。虽然语言风格仍与Prolog相近,不过因Erlang语言设计的走向,Erlang成为具备函数语言特色的程序设计语言。
1998年起,Erlang发布开放源码版本,称为开源电信平台。开源电信平台采用修改过的Mozilla公共许可证协议发放,同时爱立信仍然提供商业版本的技术支持。目前,Erlang最大的商业用户是爱立信,其他知名用户有北电网络、亚马逊以及T-Mobile等。
Erlang程序结构以函数定义为主。函数是一组将输入分别对应到输出的规则,对应方式遵守数学函数的惯例。此外,Erlang语言由几项构句要素所组成,包括文字(或称原子)、数字、列表、值组、字符、字符串、二进制资料、模块、与特定用途的关键字如fun ... end, if ... end, case ... of ... end, spawn, !, receive ... end等等。以下段落分别列示并举例说明Erlang程序的基本构成部分,涵盖资料格式、表达式格式与内置函数。
用途见以下“函数式程序设计”小节。
-module(模組名稱).-export( ).-import( 模組名稱, ).
模块名称和函数名称都是原子。 -module(模块名称) 定义模块的名字,要与文件名相同。 -export( ... ) 定义模块发布的函数,模块内的任何函数必须要发布才能让外部透过模块调用该函数。 -import( ... ) 定义本模块要从其他模块导入哪些函数,以便本模块自己使用。另外,为了方便程序的撰写并测试,还容许 -compile(export_all) 定义本模块的所有函数全部对外发布。
-compile(export_all).
(略) | 宏 | 宏是将一项资料以另一个文字做为代名。-define ( 代名 , 資料 ). ? 代名 ?MODULE | - test() -> ?hello.
|
表达式格式
类型 | 构词规则 | 例子 |
---|
变量 | 变量是一种提供与资料绑定、赋值的词汇。Erlang的变量是单一赋值,一个变量只能赋值一次。 函数是由一或多项对应规则组成。每一项规则是将一部分匹配样式的输入映射到相对的输出。 原子 ( 變數 , 變數 , ... ) -> 表達式 , 表達式 , ...在 -> 左邊是函數名稱及搭配的參數列,右邊為函數本體。 規則 ; 規則 ; ... ; 規則 .以分號分隔一或多項規則,並最後以句號結束。同一函數的每一規則必須以相同的原子開頭,並接受相同數量的參數列。 函数被调用时,会让调用方依序对被调用方的每一条函数规则做样式匹配,比对函数名称、参数数目、参数样式等等。首先完成匹配的函数规则会被运行,并且后面的函数规则会被忽略。 原子 ( 資料 , 資料 , ... )表示函數名稱及搭配的參數列。呼叫符合函數名稱及相同參數數目的函數。 函数调用时,所给予的参数可能是已赋值的变量。并且,如果参数是变量,必须是已赋值的变量。 真值比较的结果,如果成功则传回true原子,失败则传回false原子。 请记得,Erlang是以true和false表示布尔资料类型。 防卫式是接在when关键字之后的一组表达式,借由防卫式的真伪值做程控处理。防卫式的原则如下方所述: 函数对应规则格式为: 原子 ( 變數 , 變數 , ... ) -> 表達式 , 表達式 , ... 若一条函数规则加上防卫式,此规则的处理范围会多一些限制。受防卫式限制的函数对应规则格式为: 原子 ( 變數 , 變數 , ... ) when 防衛式 -> 表達式 , 表達式 , ... | - atom_pair(A, B) when is_atom(A), is_atom(B) -> {A, B}.
|
行后注解 | 任何 % 符号开头,往后到行尾的文字皆为注解文字。 | 'H.W.'. % Hello, World! |
λ演算式 | λ演算式是匿名函数,在Erlang以 fun ... end 关键字叙述。格式为: fun ( 變數 , 變數 , ... ) -> 表達式 , 表達式 , ... end 使用无参数的λ演算式,可以做出惰性求值的效果。 使用 if ... end 关键字叙述条件判断原则。格式为: if 防衛式 -> 表達式, 表達式, ... ; 防衛式 -> 表達式, 表達式, ... ; ...... 防衛式 -> 表達式, 表達式, ...end | - if
- is_number(A),
- round(A) == A,
- A >= 0 ->
- {natural, A};
- is_number(A) ->
- {real, A};
- is_atom(A) ->
- {word, A};
- true ->
- {unknown, A}
- end.
|
案例式 | 使用 case ... of ... end 关键字,根据一个变量的案例,带往相对的进程。格式为: case 表達式 of 樣式 -> 表達式, 表達式, ... ; 樣式 -> 表達式, 表達式, ... ; ...... 樣式 -> 表達式, 表達式, ...end | - case A of
- {natural, N} ->
- io:format("Natural number ~.10B is met.~n", );
- {real, R} ->
- io:format("Real number ~f is met.~n", );
- {word, W} ->
- io:format("\"~w\" is met.~n", );
- {unknown, U} ->
- io:format("Unknown structure ~w.~n", )
- end.
|
试误 | 使用 try ... catch ... end 关键字叙述试误的情况与结果。格式为: try 表達式 of 樣式 -> 表達式, 表達式, ... ; 樣式 -> 表達式, 表達式, ... ; ...... 樣式 -> 表達式, 表達式, ...catch 樣式(例外) -> 表達式, 表達式, ... ; 樣式(例外) -> 表達式, 表達式, ... ; ...... 樣式(例外) -> 表達式, 表達式, ...after 表達式, 表達式, ...end - 不需要使用after段落时,可省略after段落。
- 样式之后可接when防卫式。
| (略) |
接收消息 | 每个Erlang程序运行时,都可以从自己程序的邮箱中获取由其他程序送到的消息。可以使用 receive ... end 关键字接收消息,格式为: receive 樣式 -> 表達式, 表達式, ... ; 樣式 -> 表達式, 表達式, ... ; ...... 樣式 -> 表達式, 表達式, ...end | - receive
- {FromPid, stop} ->
- Result;
- {FromPid, Any} ->
- loop(FromPid, )
- end.
|
发送消息 | Erlang容许向程序发送消息。使用 ! 关键字,格式为: 程序代號 ! 訊息 - 消息可以是各种资料格式。消息资料格式可以是用各种表达式求出的值。
| - fun() ->
- receive
- X -> X
- end
- end)
|
列表解析页面存档备份,存于互联网档案馆 | 列表解析,是提供快速创建列表的语法。语法等同于集合建构式页面存档备份,存于互联网档案馆。格式为:- 若无防卫条件,列表解析中不写防卫式。
| - 运算结果为
|
内置函数
开源电信平台包括一个Erlang解释器、一个Erlang编译器、程序节点通信协议、CORBA、一个分布式数据库Mnesia页面存档备份,存于互联网档案馆、以及许多程序库。内置函数涵盖了各种方面的功能,涵盖了系统命令、资料访问、格式转换、网络通信、图形接口、 ... 等。以下列表介绍几项常用的Erlang内置函数。(参阅文件页面存档备份,存于互联网档案馆或索引)
切换到指定目录位置。
> c:cd("D:\\code").D:/code/ok
当指定目录不正确时,则保持在原目录位置。
编译指定的代码,之后加载新编译好的程序。
> c:c(test). % test.erl 必須存在於目錄位置{ok, test}> c:c(test1)../test1.erl:none: ...error
io:format / 2 | 按照指定的格式文字将资料印在标准输出端口。 > io:format("~.8B, ~c, ~s, ~.2f~n", ).40, a, hello, 3.14ok |
lists:sublist / 3 | 由列表中截取子列表。Erlang字符串是整数列表,于是本函数视同截取子字符串。 > lists:sublist("Hello, World!", 2, 2). "el" |
Hello World 程序
这是输出 Hello World 的一种方式:
-module(hello).-export().hello_world() -> io:fwrite("hello, world\n").
若要编译这个程序,将它存为一个名为 hello.erl 的文字档,然后从 Erlang终端 进行编译。不要忘了在每个命令的最后加上一个句号(.)。例如:
Erlang (BEAM) emulator version 4.9.1 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, worldok
函数式程序设计
Erlang支持函数式程序设计的一般特色,特色包括单次赋值、递归定义、λ演算与高端函数等等。Erlang函数大致写法如下,以整数阶乘模块为例:
-module(fact).-export().fac(N) when N > 1 -> N * fac(N-1);fac(1) -> 1.
以下是快速排序算法的Erlang实现:
-module(quicksort).-export().qsort() -> ;qsort() -> qsort() ++ ++ qsort().
以下是费氏数列求解函数:
-module(example).-export().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)).
函数式程序设计难免以递归计算,而消耗了大量递归堆栈空间。为了克服这个问题,一般使用累积参数与尾部递归等技巧节省递归数目:如以下例子。
-module(test).-export().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)).
函数式程序设计容许使用高端函数求解。以下例子说明Erlang实做复合函数。 ( f o g ,念作 f g 。)
'After'(F, G) -> fun(X) -> erlang:apply(F, )]) end.
- 请注意after是Erlang关键字。因此,以上函数命名为′′避开关键字。
> (example:'After'(fun test:show/1, fun test:parse/1))(3.1416).Real number 3.141600 is met.ok
平行式程序设计
Erlang最主要的特色是面向并发程序设计,强调多程序平行运作,并且以消息对彼此沟通。Erlang提供了spawn函数和 ! 、 receive ... end 等关键字,可以描述在Erlang/开源电信平台中的如何引导一些程序、并且如何让程序传递消息。此外,面向并发程序设计的精神还强调程序的容错处理,借由程序发生错误时的消息传递,使其他程序可以得知错误的发生,使方便于后续处理。以下分别介绍面向并发程序设计的一般程序撰写方式,以及错误处理的使用方式。
基本的平行程序示范如下:
ServerProcess = spawn(web, start_server, ),
- 以下是在任何程序中,对先前起动的程序送一则消息 {, } 。
ServerProcess ! {pause, 10},
- 以下是一段接收消息的程序。每个程序都拥有一份邮箱,可伫留收到的消息; receive ... end 程序片断是从程序的邮箱中取出最早伫留的消息。
receive a_message -> do_something; {data, DataContent} -> handle(DataContent); {hello, Text} -> io:format("Got hello message: ~s", ); {goodbye, Text} -> io:format("Got goodbye message: ~s", )end.收到 結果就是 ;收到 {, } 結果會呼叫 handle(DataContent) ;收到 {, } 結果教是印出 "Got hello message: ..." ,收到 {, } 結果是印出"Got goodbye message: ..." 。
以下程序,示范产生一组环状传递消息的程序。
ring_proc(Funs) -> Ns = lists:seq(1, length(Funs)), = ) || Nth <- Ns ], , Pids++) ]. func() -> receive ToPid -> func_msg_(ToPid) end.func_msg_(ToPid) -> receive stop -> io:format("Stop process ~w~n", ), ToPid ! stop; Message -> io:format("~w: transmit message to ~w~n", ), ToPid ! Message, func_msg_(ToPid) end.接收訊息,就對下一個程序送訊息;接收到其他任何訊息,就對下一個程序送同樣的訊息。
如果发送任何其他消息,就会让所有的程序不断对下一个程序传递消息。而以下是测试发送消息的运行结果。
> = example:ring_proc().> P ! stop.Stop process <0.233.0>stopStop process <0.234.0>> Stop process <0.232.0>>
容错处理
Erlang容错处理机制,由二个步骤实现:一是将二个程序连接起来,二者之间存在一道通信管道,可提供错误消息的传递 ── 在此使用link/1函数;二是将程序回报错误的机制打开 ── 在此使用process_flag/2函数。
-module(example).-compile(export_all).hello() -> = spawn(?MODULE, world, ), link(), ... .執行時,以 Pid = spawn(, , ) 啟動程序,此程序將啟動另一個程序,並且與它連接。
- 但以上程序还不会有错误消息的传递机制,因为回报错误的开关还没有打开。
以上 hello/0 函數前段使用process_flag/2函數,將標籤打開,即可開啟程序回報錯誤機制。
hello() -> process_flag(, ), Pid = spawn(?MODULE, world, ), link(Pid), ... .
于是,当程序结束时,会提交{'EXIT', , }资料。程序正常结束时,为。
另外,spawn函数另外有程序连接版本,spawn_link函数,同时引导并连接到新程序。
Erlang提供分布式机制,能在另一台电脑引导一些Erlang程序,并由本机电脑对其他电脑的Erlang程序传递消息。
$> erl -name node_1
$> erl -sname node_1
引导新的网络节点时,Erlang使用epmd (Erlang端口号对应管理系统) 指派端口号,提供节点使用。
当知道一个网络节点名称时,可以在该节点产生新程序。
RemoteProcess = spawn(, web, start_server, ),在遠端節點產生新程序之後,可以使用平行式程式設計的技巧,與遠端程序通訊。
Erlang / 开源电信平台提供的程序库,于分布式程序设计可以使用、、、... 等模块,做网络通信。
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.
应用
- Wings 3D,一个用Erlang编写的三维计算机图形软件。
- YAWS页面存档备份,存于互联网档案馆,以Erlang编写的高效HTTP服务器。
- DISCO页面存档备份,存于互联网档案馆,以Erlang编写的MapReduce架构系统。
- Apache CouchDB页面存档备份,存于互联网档案馆,以Erlang编写的MapReduce文件式数据库系统。
- RabbitMQ页面存档备份,存于互联网档案馆,能搭配Erlang运作的消息队列系统。
- 开放电信平台
- WhatsApp:其后端服务器应用使用了Erlang及FreeBSD。支持了4.5亿的活跃用户
- ejabberd页面存档备份,存于互联网档案馆,世界上最流行的XMPP即时通讯服务器
- EMQX页面存档备份,存于互联网档案馆,以Erlang编写的高可用、分布式MQTT消息服务器。