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.