跳轉到內容

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.
華夏公益教科書