• 沒有找到結果。

第六章 模糊控制器的軟體模式策略

6.1 歸屬函數類別

歸屬函數是描述模糊集合的函數,而每一個模糊變數之中,都至 少定義了一個歸屬函數來表示該模糊變數的某個分割區域,一般說 來,常用的歸屬函數主要有如圖 6-1 四種形式:

Z-Type S-Type

Lambda-Type Pi-Type

上述的四種歸屬函數各有各的特點(光型式就不同了),不過,

以物件導向的角度來看,我們先來看看這四種歸屬函數有什麼彼此之 間“共同”的地方:

1. 應該要有名稱:例如在描述“年齡”時,如“老”或“年幼”這 類的名詞。

2. 歸屬函數的最大與最小值:模糊集合所描述的“論域”的範圍。

在此我們將四種歸屬函數的共同部份“抽”出來,定義一個“基 礎類別”(base class),我們先將它命名為“BaseFunc”,這個類別 的宣告部份如下:

class BaseFunc {

private:

string m_FuncName; //記錄此歸屬函數的名稱 double m_Min; //記錄歸屬函數最小值

double m_Max; //記錄歸屬函數最大值 public:

void SetFuncName(string); //設定歸屬函數名稱 string GetFuncName( ); //取得歸屬函數名稱

void SetMinValue(double Min); //設定歸屬函數範圍最小值 double GetMinValue( ); //取得歸屬函數範圍最小值

void SetMaxValue(double Max); //設定歸屬函數範圍最大值 double GetMaxValue( ); //取得歸屬函數範圍最大值

virtual void DoSetLZeroValue(double Value);

virtual double DoGetLZeroValue( );

virtual void DoSetLOneValue(double Value);

virtual double DoGetLOneValue( );

virtual void DoSetRZeroValue(double Value);

virtual double DoGetRZeroValue( ); //續上頁程式碼 virtual void DoSetROneValue(double Value);

virtual double DoGetROneValue( );

virtual void DoSetMOneValue(double Value);

virtual double DoGetMOneValue( );

virtual Crisp* DoGetCrisp(double membership);

virtual double DoGetResult(double Value);

};

我們先站在物件必須為自己“負責”的觀點來看,屬於 BaseFunc 類別的物件要負責些什麼? 前文提到,能夠有接受“命名”的行為以 及能夠接受“設定範圍”的行為。有了接受外來設定的行為,所以很 自然的,在 BaseFunc 類別的私有(private)區段,也就擁有記錄這 些資訊的資料成員(date member)。

方才我們所訂定的,是四種歸屬函數的共同部份,也就是不論歸 屬函數所表現出來的是什麼樣的行為模式,它們所必須要有,也一定 會有的相同行為,接下來,基礎類別定好了,再接著就是衍生類別了

(derived class)。

訂定 BaseFunc 類別的衍生類別之前,先來看看四種歸屬函數

“同名異型”的部份。首先,我們先看看 BaseFunc 類別的 public 區 段裡的一道宣告:

virtual double DoGetResult(double Value); //虛擬函式

此成員函式的功能,主要是計算歸屬函數的結果,例如說如果現在有

計算出“0.8”(函式回傳值)的歸屬度。但是,上述的函式只不過 是一個介面(interface)而已,因為因應不同種類的歸屬函數,實作 也應該要有所不同。

首先先訂定第一個衍生類別 ZType(描述 Z-Type 型式的歸屬函 數),說明之前,ZType 類別的原型(prototype)如下:

class ZType : public BaseFunc //繼承 BaseFunc 類別 {

private:

double m_MembershipZero;

double m_MembershipOne;

public:

virtual void DoSetRZeroValue(double Value);

virtual double DoGetRZeroValue();

virtual void DoSetROneValue(double Value);

virtual double DoGetROneValue();

virtual Crisp* DoGetCrisp(double membership);

virtual double DoGetResult(double Value); //覆寫父類別的同名函式 };

在此 ZType 類別中的 DoGetResult 成員函式,將會覆寫(overriding)

它的父類別 BaseFunc 的 DoGetResult 成員函式(即使它沒有實作),

如果我們在 client 端這樣使用:

BaseFunc* aPoint = new ZType;

double temp = aPoint->DoGetResult(25);

此時雖然 aPoint 是一個 BaseFunc 型別的指標變數,但是由於它所擁 有的指標(pointer)是指向一個 ZType 型別的物件,所以第二行的呼

叫 DoGetResult 函式將會“透過”BaseFunc 直接去呼叫 ZType所擁有 的 DoGetResult 函式。

如此一來就方便了許多,因為諸如 SType 型、PiType 型、

LambdaType 型的歸屬函數在計算說歸屬度時,方法都不盡相同,但 是我們可以使用相同的介面,來達到不同的效果,而這也正是多型

(polymorphism)的精神所在。

先說明一些方才程式碼中的一些函式和變數命名的方式、如 DoGetROneValue、m_MembershipZero 等等的名詞。這些名詞的命名 方式,以圖 6-2 來作說明:

a b c d e f g

圖 6-2 名詞命名的說明範例

a 即是所謂的 LZero,因為它是在歸屬函數左緣的歸屬度 0 的位置,

相對的,b 就是 LOne,指左緣歸屬度 1 的位置,同理 c 和 d 就分別 為 ROne 和 RZero。而 f 為由於分不出左右緣,所以以 MOne 稱之。

如果不需分左右緣的話(如 ZType 和 SType),就直接以 One 和 Zero

總之,有系統的命名方式是為了要突顯出資料成員或成員函式所 要實作的功能,例如在函式之前有“Do”字樣的,指的是該函式為

“虛擬函式”(virtual function),而在資料成員的部份,在變數名稱 前加上“m”的,指的是該變數為一個 member,如果有加上“p”,

是指該變數為一指標,像這樣使用前置字的方式,稱之為匈牙利命名 法(Hungarian notation),另一方面,這樣的表示法也有助於了解程 式,日後維護程式時也效為方便。

回到 ZType 類別宣告部份,除了覆寫父類別的 DoGetResult 函式 之外,另外也覆寫了以下五個虛擬函式:

virtual void DoSetRZeroValue(double Value);

virtual double DoGetRZeroValue();

virtual void DoSetROneValue(double Value);

virtual double DoGetROneValue();

virtual Crisp* DoGetCrisp(double membership);

如字面上所示的,是設定以及取得右緣的歸屬度 0 和 1 位置的明確 值,只有右緣是因為 Z-Type 型的歸屬函數只有右緣的部份,如圖 6-3:

ROne RZero

圖 6-3 Z-Type 型的歸屬函數命名原則

而這些虛擬函式,在 ZType 類別的父類別 BaseFunc 中,也有同名的 宣告。

在上述的函式中,DoGetCrisp 函式的用途是用來反向演算,是在 得到一個歸屬值的情況下,求得“兩個”明確值,為什麼是兩個呢?

如圖 6-4 所示:

假設為 Z-Type 的範圍最小值 假設為 S-Type 的範圍最大值

a1 a2 b1 b2 c1 c2 d1 d2

圖 6-4 以歸屬值求明確值時的情況

只要有一歸屬值,反推回來的明確值就一定是兩個,所以 DoGetCrisp 的回傳值為一個 Crisp 的結構指標,Crisp 結構的定義如下:

struct Crisp {

double m_min; //記錄較小值 double m_max; //記錄較大值 };

最後,以 UML 來表示歸屬函數之間的類別繼承關係(下頁圖 6-5):

64

Class BaseFunc

SetFuncName(string) : void GetFuncName( ) : string SetMinValue(double) : void GetMinValue( ) : double SetMaxValue(double) : void GetMaxValue( ) : double

DoSetLZeroValue(double) : void DoGetLZeroValue( ) : double DoSetLOneValue(double) : void DoGetLOneValue( ) : double DoSetRZeroValue(double) : void DoGetRZeroValue( ) : double DoSetROneValue(double) : void DoGetROneValue( ) : double DoSetMOneValue(double) : void DoGetMOneValue( ) : double DoGetCrisp(double) : Crisp*

Class SType

DoSetLZeroValue(double) : void DoGetLZeroValue( ) : double DoSetLOneValue(double) : void DoGetLOneValue( ) : double DoGetCrisp(double) : Crisp*

DoGetResult(double) : double

Class ZType

DoSetRZeroValue(double) : void DoGetRZeroValue( ) : double DoSetROneValue(double) : void DoGetROneValue( ) : double DoGetCrisp(double) : Crisp*

DoGetResult(double) : double

Class PiType

DoSetLZeroValue(double) : void DoGetLZeroValue( ) : double DoSetLOneValue(double) : void DoGetLOneValue( ) : double DoSetRZeroValue(double) : void DoGetRZeroValue( ) : double DoSetROneValue(double) : void DoGetROneValue( ) : double DoGetCrisp(double) : Crisp*

DoGetResult(double) : double

Class LambdaType

DoSetLZeroValue(double) : void DoGetLZeroValue( ) : double DoSetRZeroValue(double) : void DoGetRZeroValue( ) : double DoSetMOneValue(double) : void DoGetMOneValue( ) : double DoGetCrisp(double) : Crisp*

DoGetResult(double) : double m_LMembershipZero :double m_RMembershipZero :double m_MmembershipOne :double

m_LMembershipZero : double m_LMembershipOne : double m_RMembershipZero : double m_RMembershipOne : double m_MembershipZero : double m_MembershipOne : double m_FuncName : string

m_Min : double m_Max : double

m_MembershipZero : double m_MembershipOne : double