源代碼下載:?erlang-cn.erl
% 百分比符號標明注釋的開始。
%% 兩個符號通常用于注釋函數(shù)。
%%% 三個符號通常用于注釋模塊。
% Erlang 里使用三種標點符號:
% 逗號 (`,`) 分隔函數(shù)調(diào)用中的參數(shù)、數(shù)據(jù)構(gòu)建和模式。
% 句號 (`.`) (后跟空格)分隔函數(shù)和 shell 中的表達式。
% 分號 (`;`) 分隔語句。以下環(huán)境中使用語句:
% 函數(shù)定義和`case`、`if`、`try..catch`、`receive`表達式。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 1\. 變量和模式匹配
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Num = 42. % 變量必須以大寫字母開頭。
% Erlang 的變量只能賦值一次。如果給變量賦不同的值,會導致錯誤:
Num = 43. % ** exception error: no match of right hand side value 43
% 大多數(shù)語言中`=`表示賦值語句,在Erlang中,則表示模式匹配。
% `Lhs = Rhs`實際上意味著:
% 演算右邊(Rhs), 將結(jié)果與左邊的模式匹配。
Num = 7 * 6.
% 浮點數(shù)
Pi = 3.14159.
% Atoms 用于表示非數(shù)字的常量。
% Atom 以小寫字母開始,包含字母、數(shù)字、`_`和`@`。
Hello = hello.
OtherNode = example@node.
% Atom 中如果包含特殊字符,可以用單引號括起。
AtomWithSpace = 'some atom with space'.
% Erlang 的元組類似 C 的 struct.
Point = {point, 10, 45}.
% 使用模式匹配操作符`=`獲取元組的值。
{point, X, Y} = Point. % X = 10, Y = 45
% 我們可以使用`_`存放我們不感興趣的變量。
% `_`被稱為匿名變量。和其他變量不同,
% 同一個模式中的多個`_`變量不必綁定到相同的值。
Person = {person, {name, {first, joe}, {last, armstrong}}, {footsize, 42}}.
{_, {_, {_, Who}, _}, _} = Person. % Who = joe
% 列表使用方括號,元素間使用逗號分隔。
% 列表的元素可以是任意類型。
% 列表的第一個元素稱為列表的 head,其余元素稱為列表的 tail。
ThingsToBuy = [{apples, 10}, {pears, 6}, {milk, 3}].
% 若`T`是一個列表,那么`[H|T]`同樣是一個列表,head為`H`,tail為`T`.
% `|`分隔列表的 head 和 tail.
% `[]`是空列表。
% 我們可以使用模式匹配操作來抽取列表中的元素。
% 如果我們有一個非空的列表`L`,那么`[X|Y] = L`則
% 抽取 L 的 head 至 X,tail 至 Y (X、Y需為未定義的變量)。
[FirstThing|OtherThingsToBuy] = ThingsToBuy.
% FirstThing = {apples, 10}
% OtherThingsToBuy = {pears, 6}, {milk, 3}
% Erlang 中的字符串其實是由整數(shù)組成的數(shù)組。字符串使用雙引號。
Name = "Hello".
[72, 101, 108, 108, 111] = "Hello".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 2\. 循序編程
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Module 是 Erlang 代碼的基本單位。我們編寫的所有函數(shù)都儲存在 module 中。
% Module 存儲在后綴為 `.erl` 的文件中。
% Module 必須事先編譯。編譯好的 module 以 `.beam` 結(jié)尾。
-module(geometry).
-export([area/1]). % module 對外暴露的函數(shù)列表
% `area`函數(shù)包含兩個分句,分句間以分號相隔。
% 最后一個分句以句號加換行結(jié)尾。
% 每個分句由頭、體兩部門組成。
% 頭部包含函數(shù)名稱和用括號括起的模式,
% 體部包含一系列表達式,如果頭部的模式和調(diào)用時的參數(shù)匹配,這些表達式會被演算。
% 模式匹配依照定義時的順序依次進行。
area({rectangle, Width, Ht}) -> Width * Ht;
area({circle, R}) -> 3.14159 * R * R.
% 編譯文件為 geometry.erl.
c(geometry). % {ok,geometry}
% 調(diào)用函數(shù)時必須使用 module 名和函數(shù)名。
geometry:area({rectangle, 10, 5}). % 50
geometry:area({circle, 1.4}). % 6.15752
% 在 Erlang 中,同一模塊中,參數(shù)數(shù)目不同,名字相同的函數(shù)是完全不同的函數(shù)。
-module(lib_misc).
-export([sum/1]). % 對外暴露的`sum`函數(shù)接受一個參數(shù):由整數(shù)組成的列表。
sum(L) -> sum(L, 0).
sum([], N) -> N;
sum([H|T], N) -> sum(T, H+N).
% fun 是匿名函數(shù)。它們沒有名字,不過可以賦值給變量。
Double = fun(X) -> 2*X end. % `Double` 指向匿名函數(shù) #Fun<erl_eval.6.17052888>
Double(2). % 4
% fun 可以作為函數(shù)的參數(shù)和返回值。
Mult = fun(Times) -> ( fun(X) -> X * Times end ) end.
Triple = Mult(3).
Triple(5). % 15
% 列表解析是創(chuàng)建列表的表達式(不使用fun、map 或 filter)。
% `[F(X) || X <- L]` 表示 "由 `F(X)` 組成的列表,其中 `X` 取自列表 `L`。
L = [1,2,3,4,5].
[2*X || X <- L]. % [2,4,6,8,10]
% 列表解析可以使用生成器,也可以使用過濾器,過濾器用于篩選列表的一部分。
EvenNumbers = [N || N <- [1, 2, 3, 4], N rem 2 == 0]. % [2, 4]
% Guard 是用于增強模式匹配的結(jié)構(gòu)。
% Guard 可用于簡單的測試和比較。
% Guard 可用于函數(shù)定義的頭部,以`when`關(guān)鍵字開頭,或者其他可以使用表達式的地方。
max(X, Y) when X > Y -> X;
max(X, Y) -> Y.
% guard 可以由一系列 guard 表達式組成,這些表達式以逗號分隔。
% `GuardExpr1, GuardExpr2, ..., GuardExprN` 為真,當且僅當每個 guard 表達式均為真。
is_cat(A) when is_atom(A), A =:= cat -> true;
is_cat(A) -> false.
is_dog(A) when is_atom(A), A =:= dog -> true;
is_dog(A) -> false.
% guard 序列 `G1; G2; ...; Gn` 為真,當且僅當其中任意一個 guard 表達式為真。
is_pet(A) when is_dog(A); is_cat(A) -> true;
is_pet(A) -> false.
% Record 可以將元組中的元素綁定到特定的名稱。
% Record 定義可以包含在 Erlang 源代碼中,也可以放在后綴為`.hrl`的文件中(Erlang 源代碼中 include 這些文件)。
-record(todo, {
status = reminder, % Default value
who = joe,
text
}).
% 在定義某個 record 之前,我們需要在 shell 中導入 record 的定義。
% 我們可以使用 shell 函數(shù)`rr` (read records 的簡稱)。
rr("records.hrl"). % [todo]
% 創(chuàng)建和更新 record。
X = #todo{}.
% #todo{status = reminder, who = joe, text = undefined}
X1 = #todo{status = urgent, text = "Fix errata in book"}.
% #todo{status = urgent, who = joe, text = "Fix errata in book"}
X2 = X1#todo{status = done}.
% #todo{status = done,who = joe,text = "Fix errata in book"}
% `case` 表達式。
% `filter` 返回由列表`L`中所有滿足`P(x)`為真的元素`X`組成的列表。
filter(P, [H|T]) ->
case P(H) of
true -> [H|filter(P, T)];
false -> filter(P, T)
end;
filter(P, []) -> [].
filter(fun(X) -> X rem 2 == 0 end, [1, 2, 3, 4]). % [2, 4]
% `if` 表達式。
max(X, Y) ->
if
X > Y -> X;
X < Y -> Y;
true -> nil;
end.
% 警告: `if` 表達式里至少有一個 guard 為真,否則會觸發(fā)異常。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 3\. 異常
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 當遇到內(nèi)部錯誤或顯式調(diào)用時,會觸發(fā)異常。
% 顯式調(diào)用包括 `throw(Exception)`, `exit(Exception)` 和
% `erlang:error(Exception)`.
generate_exception(1) -> a;
generate_exception(2) -> throw(a);
generate_exception(3) -> exit(a);
generate_exception(4) -> {'EXIT', a};
generate_exception(5) -> erlang:error(a).
% Erlang 有兩種捕獲異常的方法。其一是將調(diào)用包裹在`try...catch`表達式中。
catcher(N) ->
try generate_exception(N) of
Val -> {N, normal, Val}
catch
throw:X -> {N, caught, thrown, X};
exit:X -> {N, caught, exited, X};
error:X -> {N, caught, error, X}
end.
% 另一種方式是將調(diào)用包裹在`catch`表達式中。
% 此時異常會被轉(zhuǎn)化為一個描述錯誤的元組。
catcher(N) -> catch generate_exception(N).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 4\. 并發(fā)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Erlang 依賴于 actor并發(fā)模型。在 Erlang 編寫并發(fā)程序的三要素:
% 創(chuàng)建進程,發(fā)送消息,接收消息
% 啟動一個新的進程使用`spawn`函數(shù),接收一個函數(shù)作為參數(shù)
F = fun() -> 2 + 2 end. % #Fun<erl_eval.20.67289768>
spawn(F). % <0.44.0>
% `spawn` 函數(shù)返回一個pid(進程標識符),你可以使用pid向進程發(fā)送消息。
% 使用 `!` 操作符發(fā)送消息。
% 我們需要在進程內(nèi)接收消息,要用到 `receive` 機制。
-module(caculateGeometry).
-compile(export_all).
caculateAera() ->
receive
{rectangle, W, H} ->
W * H;
{circle, R} ->
3.14 * R * R;
_ ->
io:format("We can only caculate area of rectangles or circles.")
end.
% 編譯這個模塊,在 shell 中創(chuàng)建一個進程,并執(zhí)行 `caculateArea` 函數(shù)。
c(caculateGeometry).
CaculateAera = spawn(caculateGeometry, caculateAera, []).
CaculateAera ! {circle, 2}. % 12.56000000000000049738
% shell也是一個進程(process), 你可以使用`self`獲取當前 pid
self(). % <0.41.0>
更多建議: