跳转到内容

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.
华夏公益教科书