Erlang 程式設計/示例 1
使用 Erlang 的物件
Erlang 是一種函數語言程式設計語言和麵向併發程式語言(Armstrong,論文,2005),但 Erlang 沒有顯式內建的面嚮物件語言特性。可以使用替代方法輕鬆實現面向物件程式設計風格。如果我們只限於單一繼承,那麼面向物件程式設計尤其容易實現。可以使用程序來表示類,使用訊息來表示方法。為此,在建立每個物件時,可以建立一個表示其繼承鏈中的祖先的程序鏈。方法(訊息)可以沿著鏈傳遞,直到到達擁有匹配方法的程序。如果訊息到達鏈的頂端(Object 類或高於 dev_nul 的類),那麼我們可以生成一個“錯誤方法名”異常。每個類(程序)將在自己的遞迴引數呼叫列表中維護自己的屬性(變數)。可以使用 get 和 set 等訊息來訪問和更新這些屬性。屬性儲存在每個類程序中的名為 Bundle 的字典中。
包括一些使用所述技術建立 OOP 的示例程式碼。在程式中,我們建立了整數類的例項。它的父類 float 知道如何對實數開平方根。它的父類 complex 知道如何對負數開平方根。它的父類 matrix 知道如何對對角矩陣開平方根。
從邏輯上講,傳統的類關係儲存在類圖中。整數是實數。實數(float)是(複數的)子集。如果我們將(1×1)矩陣視為單個數字,那麼複數是(複數矩陣的)子集。
訊息(方法)傳送到物件的例項。如果程序不知道如何做某事,它會將其訊息(方法)傳遞給其父級(這種情況下的程序)。如果我們試圖做一些類似於對非對角矩陣開方根的操作,它將被傳遞到 dev_nul 並生成錯誤。
start 函式建立整數類的例項。然後它要求例項計算 4 個數字的平方根:4、0.04、-4 和矩陣 [[4,0],[0,9]]。答案是:2、0.2、{2, i} 和 [[2,0],[0,3]]。
----------------------------------------------------------------
Original Each object has its own process for each of its ancestors
Classes (Process chain for object Obj_1)
+---------+ +---------+
| dev_nul | | dev_nul |
+---------+ +---------+
/ \ / \
| |
| |
+-----------+ +-----------+
| Object | | Object |
+-----------+ +-----------+
| id | | id |
| classname | | classname |
| name | | name |
+-----------+ +-----------+
| get() | | get() |
+-----------+ +-----------+
/ \ / \
| |
| |
+---------+ +---------+
| Matrix | | Matrix |
+---------+ +---------+
+---------+ +---------+
| sqrt() | | sqrt() |
+---------+ +---------+
/ \ / \
| |
| |
+---------+ +---------+
| Complex | | Complex |
+---------+ +---------+
+---------+ +---------+
| sqrt() | | sqrt() |
+---------+ +---------+
/ \ / \
| |
| |
+---------+ +---------+
| Float | | Float |
+---------+ +---------+
+---------+ +---------+
| sqrt() | | sqrt() |
+---------+ +---------+
/ \ / \
| |
| |
+---------+ +---------+
| Integer | | Integer |
+---------+ +---------+
+---------+ +---------+
| sqrt() | | sqrt() |
+---------+ +---------+
---------------------------------------
Program output:
1> mathobjects:start().
[
[{id,#Ref<0.0.0.27>},{class_name,integer},{name,book}],
2.00000, 0.20000,
{2.00000, i},
[[2.00000,0],[0,3.00000]]
]
--------------------------------------
-module(mathobjects).
-compile(export_all).
start()->
Obj_1 = spawn_link(mathobjects, integer, []),
Id_1 = rpc(Obj_1, {get, id}),
Name_1 = rpc(Obj_1, {get, name}),
Class_Name_1 = rpc(Obj_1, {get, class_name}),
% -------------------------------
R0 = [ Id_1, Class_Name_1, Name_1 ],
R1 = rpc(Obj_1, {sqrt, 4}),
R2 = rpc(Obj_1, {sqrt, 0.04}),
R3 = rpc(Obj_1, {sqrt, -4}),
R4 = rpc(Obj_1, {sqrt, [[4,0],[0,9]]}),
[R0, R1, R2, R3, R4].
rpc(Dest, Msg) ->
Dest ! {self(), Msg},
receive
Answer ->
Answer
after 1000 ->
ok
end.
dev_null() ->
receive
{From, Any} -> From ! {Any, attribute_unknown}
end,
dev_null().
object() ->
Id = erlang:make_ref(),
Class_Name = object,
Bundle = dict:from_list([ {id,Id}, {class_name, Class_Name} ]),
Parent = spawn(objects, dev_null, []),
object(Parent, Bundle).
object(Parent, Bundle) ->
receive
{From, {get, Attribute}} ->
handle_get_attribute(Attribute, From, Bundle, Parent)
end,
object(Parent, Bundle).
% default constructor
matrix() ->
Class_Name = matrix,
Name = book,
Parent = spawn_link(mathobjects, object, []),
Parent_Class = object,
Bundle = dict:from_list( [
{class_name, Class_Name},
{parent_class, Parent_Class},
{name, Name},
{parent, Parent}]),
matrix(Parent, Bundle).
matrix(Parent, Bundle) ->
receive
{From, {get, Attribute}} ->
handle_get_attribute(Attribute, From, Bundle, Parent);
{set, Attribute, Value} ->
NBundle = handle_set_attribute(Attribute, Value, Bundle, Parent),
matrix(Parent, NBundle);
{From, {sqrt, [[A,B],[C,D]]}} when B==0, C==0 ->
Out = [[math:sqrt(A),0],[0,math:sqrt(D)]],
From ! Out;
Any ->
Parent ! Any
end,
matrix(Parent, Bundle).
complex() ->
Class_Name = complex,
Name = book,
Parent = spawn_link(mathobjects, matrix, []),
Parent_Class = object,
Bundle = dict:from_list( [
{class_name, Class_Name},
{parent_class, Parent_Class},
{name, Name},
{parent, Parent} ] ),
complex(Parent, Bundle).
complex(Parent, Bundle) ->
receive
{From, {get, Attribute}} ->
handle_get_attribute(Attribute, From, Bundle, Parent);
{set, Attribute, Value} ->
NBundle = handle_set_attribute(Attribute, Value, Bundle, Parent),
complex(Parent, NBundle);
{From, {sqrt, Arg}} when is_list(Arg) ->
Parent ! {From, {sqrt, Arg}};
{From, {sqrt, Arg}} when Arg < 0 ->
Out = {math:sqrt(0-Arg), i},
From ! Out;
Any ->
Parent ! Any
end,
complex(Parent, Bundle).
float() ->
Class_Name = float,
Name = book,
Parent = spawn_link(mathobjects, complex, []),
Parent_Class = object,
Bundle = dict:from_list( [
{class_name, Class_Name},
{parent_class, Parent_Class},
{name, Name},
{parent, Parent}]),
float(Parent, Bundle).
float(Parent, Bundle) ->
receive
{From, {get, Attribute}} ->
handle_get_attribute(Attribute, From, Bundle, Parent);
{set, Attribute, Value} ->
NBundle = handle_set_attribute(Attribute, Value, Bundle, Parent),
float(Parent, NBundle);
{From, {sqrt, Arg}} when is_list(Arg) ->
Out = rpc(Parent, {sqrt, Arg}),
From ! Out;
{From, {sqrt, Arg}} when Arg < 0 ->
Out = rpc(Parent, {sqrt, Arg}),
From ! Out;
{From, {sqrt, Arg}} ->
Out = math:sqrt(Arg),
From ! Out;
Any ->
Parent ! Any
end,
float(Parent, Bundle).
integer() ->
Class_Name = integer,
Name = book,
Parent = spawn_link(mathobjects, float, []),
Parent_Class = object,
Bundle = dict:from_list( [
{class_name, Class_Name},
{parent_class, Parent_Class},
{name, Name},
{parent, Parent}]),
integer(Parent, Bundle).
integer(Parent, Bundle) ->
receive
{From, {get, Attribute}} ->
handle_get_attribute(Attribute, From, Bundle, Parent);
{set, Attribute, Value} ->
NBundle = handle_set_attribute(Attribute, Value, Bundle, Parent),
integer(Parent, NBundle);
{From, {sqrt, Arg}} when is_float(Arg) ->
Out = rpc(Parent, {sqrt, Arg}),
From ! Out;
{From, {sqrt, Arg}} when is_list(Arg) ->
Out = rpc(Parent, {sqrt, Arg}),
From ! Out;
{From, {sqrt, Arg}} when Arg < 0 ->
Out = rpc(Parent, {sqrt, Arg}),
From ! Out;
{From, {sqrt, Arg}} ->
Out =
try math:sqrt(Arg)
catch
_AnyException ->
rpc(Parent, {From, sqrt, Arg})
end,
From ! Out;
Any ->
Parent ! Any
end,
integer(Parent, Bundle).
% -----------------------------------------------
handle_set_attribute(Attribute, Value, Bundle, Parent) ->
Found = dict:find(Attribute, Bundle),
if
is_tuple(Found) -> % if attribute exists then set it
{ok, _} = Found,
NBundle = dict:store(Attribute, Value, Bundle),
NBundle;
true ->
Parent ! {set, Attribute, Value}
end.
handle_get_attribute(Attribute, From, Bundle, Parent) ->
Found = dict:find(Attribute, Bundle),
if
is_tuple(Found) ->
{ok, Value} = Found,
From ! {Attribute, Value};
true ->
Parent ! {From, {get, Attribute}}
end.