Y分鐘Erlang


language: erlang
lang: zh-cn
contributors:html

- ["Giovanni Cappellotto", "http://www.focustheweb.com/"]

translators:node

- ["Jakukyo Friel", "http://weakish.github.io"]

filename: erlang-cn.erl

% 百分比符號標明註釋的開始。git

%% 兩個符號一般用於註釋函數。github

%%% 三個符號一般用於註釋模塊。web

% Erlang 裏使用三種標點符號:
% 逗號 (,) 分隔函數調用中的參數、數據構建和模式。
% 句號 (.) (後跟空格)分隔函數和 shell 中的表達式。
% 分號 (;) 分隔語句。如下環境中使用語句:
% 函數定義和caseiftry..catchreceive表達式。shell

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%編程

1. 變量和模式匹配

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%數組

Num = 42.  % 變量必須以大寫字母開頭。

% Erlang 的變量只能賦值一次。若是給變量賦不一樣的值,會致使錯誤:
Num = 43. % ** exception error: no match of right hand side value 43

% 大多數語言中`=`表示賦值語句,在Erlang中,則表示模式匹配。
% `Lhs = Rhs`實際上意味着:
% 演算右邊(Rhs), 將結果與左邊的模式匹配。
Num = 7 * 6.

% 浮點數
Pi = 3.14159.

% Atoms 用於表示非數字的常量。
% Atom 以小寫字母開始,包含字母、數字、`_`和`@`。
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 中的字符串實際上是由整數組成的數組。字符串使用雙引號。
Name = "Hello".
[72, 101, 108, 108, 111] = "Hello".

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%併發

2. 循序編程

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%app

% Module 是 Erlang 代碼的基本單位。咱們編寫的全部函數都儲存在 module 中。
% Module 存儲在後綴爲 `.erl` 的文件中。
% Module 必須事先編譯。編譯好的 module 以 `.beam` 結尾。
-module(geometry).
-export([area/1]). % module 對外暴露的函數列表

% `area`函數包含兩個分句,分句間以分號相隔。
% 最後一個分句以句號加換行結尾。
% 每一個分句由頭、體兩部門組成。
% 頭部包含函數名稱和用括號括起的模式,
% 體部包含一系列表達式,若是頭部的模式和調用時的參數匹配,這些表達式會被演算。
% 模式匹配依照定義時的順序依次進行。
area({rectangle, Width, Ht}) -> Width * Ht;
area({circle, R})            -> 3.14159 * R * R.

% 編譯文件爲 geometry.erl.
c(geometry).  % {ok,geometry}

% 調用函數時必須使用 module 名和函數名。
geometry:area({rectangle, 10, 5}).  % 50
geometry:area({circle, 1.4}).  % 6.15752

% 在 Erlang 中,同一模塊中,參數數目不一樣,名字相同的函數是徹底不一樣的函數。
-module(lib_misc).
-export([sum/1]). % 對外暴露的`sum`函數接受一個參數:由整數組成的列表。
sum(L) -> sum(L, 0).
sum([], N)    -> N;
sum([H|T], N) -> sum(T, H+N).

% fun 是匿名函數。它們沒有名字,不過能夠賦值給變量。
Double = fun(X) -> 2*X end. % `Double` 指向匿名函數 #Fun<erl_eval.6.17052888>
Double(2).  % 4

% fun 能夠做爲函數的參數和返回值。
Mult = fun(Times) -> ( fun(X) -> X * Times end ) end.
Triple = Mult(3).
Triple(5).  % 15

% 列表解析是建立列表的表達式(不使用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 是用於加強模式匹配的結構。
% Guard 可用於簡單的測試和比較。
% Guard 可用於函數定義的頭部,以`when`關鍵字開頭,或者其餘可使用表達式的地方。
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 函數`rr` (read records 的簡稱)。
rr("records.hrl").  % [todo]

% 建立和更新 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 爲真,不然會觸發異常。

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

3. 異常

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% 當遇到內部錯誤或顯式調用時,會觸發異常。
% 顯式調用包括 `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 有兩種捕獲異常的方法。其一是將調用包裹在`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.

% 另外一種方式是將調用包裹在`catch`表達式中。
% 此時異常會被轉化爲一個描述錯誤的元組。
catcher(N) -> catch generate_exception(N).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

4. 併發

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Erlang 依賴於 actor併發模型。在 Erlang 編寫併發程序的三要素:
% 建立進程,發送消息,接收消息

% 啓動一個新的進程使用`spawn`函數,接收一個函數做爲參數

F = fun() -> 2 + 2 end. % #Fun<erl_eval.20.67289768>
spawn(F). % <0.44.0>

% `spawn` 函數返回一個pid(進程標識符),你可使用pid向進程發送消息。
% 使用 `!` 操做符發送消息。
%  咱們須要在進程內接收消息,要用到 `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 中建立一個進程,並執行 `caculateArea` 函數。
c(caculateGeometry).
CaculateAera = spawn(caculateGeometry, caculateAera, []).
CaculateAera ! {circle, 2}. % 12.56000000000000049738

% shell也是一個進程(process), 你可使用`self`獲取當前 pid

self(). % <0.41.0>

References

相關文章
相關標籤/搜索