1. 程式人生 > >設計模式C++實現(20)——直譯器模式

設計模式C++實現(20)——直譯器模式

1. 直譯器模式(Interpreter Pattern)的定義

(1)定義

  給定一個語言定義它的文法的一種表示,並定義一個直譯器,這個直譯器使用該表示來解釋語言中的句子。

  ①文法:即語法規則。在直譯器模式中每一個語法都將對應一個直譯器物件,用來處理相應的語法規則。它對於擴充套件、改變文法以及增加新的文法規則都很方便。

  ②直譯器模式描述瞭如何為簡單的語言定義一個文法如何在該語言中表示一個句子,以及如何解釋這些句子

  ③在直譯器模式中可以通過一種稱之為抽象語法樹(Abstract Syntax Tree, AST)的圖形方式來直觀地表示語言的構成,每一棵抽象語法樹對應一個語言例項

(2)直譯器模式的結構和說明

  

  ①AbstractExpression:定義直譯器的介面,約定直譯器的解釋操作。其中的Interpret介面,正如其名字那樣,它是專門用來解釋該直譯器所要實現的功能。(如加法直譯器中的Interpret介面就是完成兩個運算元的相加功能)。

  ②TerminalExpression:終結符直譯器,用來實現語法規則中和終結符相關的操作,不再包含其他的直譯器,如果用組合模式來構建抽象語法樹的話,就相當於組合模式中的葉子物件,可以有多種終結符直譯器。

  ③NonterminalExpression:非終結符直譯器,用來實現語法規則中非終結符相關的操作,通常一個直譯器對應一個語法規則,可以包含其他直譯器,如果用組合模式構建抽象語法樹的話,就相當於組合模式中的組合物件

。可以有多種非終結符直譯器。

  ④Context:上下文,通常包含各個直譯器需要的資料或是公共的功能。這個Context在直譯器模式中起著非常重要的作用。一般用來傳遞被所有直譯器共享的資料,後面的直譯器可以從這裡獲取這些值。

  ⑤Client:客戶端,指的是使用直譯器的客戶端,通常在這裡將按照語言的語法做的表示式轉換成使用直譯器物件描述的抽象語法樹,然後呼叫解釋操作。

【程式設計實驗】四則運算(注意終結符直譯器與非終結直譯器的劃分)

複製程式碼
//行為型模式:直譯器模式
//場景:四則運算

#include <iostream>
#include <string>
#include 
<map> #include <stack> #include <typeinfo> using namespace std; //*******************************************抽象表示式類*********************************** class Expression { public: //解析公式和數值,其中var中的key是公式中的引數,value值是具體的數字 //如a = 100; b = 20; c = 40 virtual int interpreter(map<string, int>& var) = 0; virtual ~Expression(){}; }; //變數解析器(終結符表示式) class VarExpression : public Expression { string key; public: VarExpression(string key) { this->key = key; } //從map中取出變數的值 int interpreter(map<string, int>& var) { return var[key]; } ~VarExpression() { cout << "~VarExpression()" << endl; } }; //**********抽象運算子號解析器*********************** //抽象運算子號解析器 class SymbolExpression : public Expression { protected: Expression* left; Expression* right; public: SymbolExpression(Expression* left, Expression* right) { this -> left = left; this -> right = right; } Expression* getLeft() { return left; } Expression* getRight() { return right; } }; //加法解析器 class AddExpression : public SymbolExpression { public: AddExpression(Expression* left, Expression* right): SymbolExpression(left,right) { } //把左右兩個表示式運算的結果加起來 int interpreter(map<string, int>& var) { return left->interpreter(var) + right ->interpreter(var); } ~AddExpression() { cout << "~AddExpression()" << endl; } }; //減法解析器 class SubExpression : public SymbolExpression { public: SubExpression(Expression* left, Expression* right): SymbolExpression(left,right) { } //把左右兩個表示式運算的結果相減 int interpreter(map<string, int>& var) { return left->interpreter(var) - right ->interpreter(var); } ~SubExpression() { cout << "~SubExpression()" << endl; } }; //*********************************解析器封裝類*************************************** //解析器封裝類,這個類是根據迪米特法則進行封裝,目的是讓Client只與直接朋友打交道,相當於Facade class Calculator { private: Expression* expression; public: //建構函式傳參,並解析表示式,構建語法樹 Calculator(string expStr) { expression = NULL; //棧,用來暫存中間結果 stack<Expression*> stkExp; Expression* left = NULL; Expression* right = NULL; /*從左到向分析表示式(如:a+b-c),最終的語法樹如下: * - * / \ * + c * / \ * a b */ for(unsigned int i = 0; i< expStr.length(); i++) { switch(expStr[i]) { case '+': //加法 //1.先從棧中取出左運算元 left = stkExp.top(); stkExp.pop(); //2.從表示式中取出+號後面的右運算元,並生成終結符解析物件 right = new VarExpression(expStr.substr(++i,1)); //3.將左右運算元相加,並把結果放入棧中 stkExp.push(new AddExpression(left, right)); break; case '-': //1.先從棧中取出左運算元 left = stkExp.top(); stkExp.pop(); //2.從表示式中取出+號後面的右運算元,並生成終結符解析物件 right = new VarExpression(expStr.substr(++i,1)); //3.將左右運算元相減,並把結果放入棧中 stkExp.push(new SubExpression(left, right)); break; default: //如果是變數(終結符):如a+b+c中的a\b\c, //則直接生成對應的變數解析器物件 stkExp.push(new VarExpression(expStr.substr(i,1))); } } //棧中儲存的就是最終語法樹的根結點(本例為SuuExpression物件) if(!stkExp.empty()) { expression = stkExp.top(); stkExp.pop(); } } void deltree(Expression* expression) { SymbolExpression* branch = dynamic_cast<SymbolExpression*>(expression); //葉子結點 if (branch == NULL) { delete expression; } else //分支結點 { //左子樹 deltree(branch->getLeft()); //右子樹 deltree(branch->getRight()); //結點 delete expression; } } ~Calculator() { deltree(expression); expression = NULL; } //開始運算 int run(map<string, int>& var) { return (expression == NULL) ? 0 : expression->interpreter(var); } }; int main() { string expStr = "a+b-c"; //為簡化處理,這裡必須是合法的表示式 map<string, int> var; //相當於Interpreter模式中的Context var["a"] = 100; var["b"] = 20; var["c"] = 40; Calculator cal(expStr); cout <<"運算結果為:" << expStr << " = " << cal.run(var) << endl; return 0; } /* 運算結果為:a+b-c = 80 ~VarExpression() ~VarExpression() ~AddExpression() ~VarExpression() ~SubExpression() */
複製程式碼

2. 思考直譯器模式

(1)直譯器模式的本質分離實現,解釋執行。通過一個直譯器物件處理一個語法規則的方式,把複雜的功能分離開,然後選擇需要被執行的功能,並把這些功能組合成需要解釋執行的抽象語法樹,再按照抽象語法樹來解釋執行,實現相應的功能。從本質上看,直譯器模式的思路仍然是分離、封裝和簡化,這與很多其他模式是一樣的。

(2)誰來構建抽象語法樹——解析器

  解析器的工作主要就是用來構建抽象語法樹的,這個角色會客戶端轉來的語言,負責按語法規則,生成一個個直譯器,並將這些直譯器組合成一顆抽象語法樹。注意,解析器的工作主要是構造語法樹,而直譯器的工作是解釋這顆語法樹

(3)誰來負責解釋操作  ——直譯器(Interpreter)

  只要定義好了抽象語法樹,肯定是直譯器來負責解釋執行,而選擇直譯器的工作,在構建抽象語法樹的時候就完成了,一般由根結點的直譯器開始解釋,然後遞迴地呼叫其他直譯器。

【程式設計實驗】中文數字轉阿拉伯數字(注意Context的應用)

 

複製程式碼
//行為型模式:直譯器模式
//場景:中文數字轉阿拉伯數字
/*
1、使用Interpreter模式來將中文數字轉換為數學數字的好處是可以應對中文數字的變化,
雖然可以用一很好的演算法將中文轉化為數字,直譯器的擴充套件效能比較好,如果出現億、兆的情況,
可以寫出兩個類(YiExpression、ZhaoExpression)來繼承Expression類,而其他地方的程式碼都不變化。
2、思路:用單位用分解出不同的直譯器.其中個位、十位、百位和千位是終結符直譯器,萬位是非結終符
         直譯器,因為萬以上的單位可以形成如果九百零一萬之類的數字,需要進一進拆分成由結終符
         構成的直譯器來完成任務。
3、轉化:將中文數字串由低位向高位方向不斷轉化

*/
#include <iostream>
#include <string>
#include <map>
#include <stack>
#include <list>

using namespace std;

//字串上下文資訊:儲存沒有處理的字串資訊
class Context
{
private:
    string statement;
    int data;
public:
    Context(string statement)
    {
        this ->statement = statement;
        data = 0;
    }

    string& getStatement()
    {
        return  statement;
    }
    void setStatement(string statement)
    {
        this -> statement = statement;
    }

    int getData()
    {
        return data;
    }

    void setData(int data)
    {
        this -> data = data;
    }
};

//抽象類:直譯器
class Expression
{
protected:
    //資料字典:儲存中文數字一到九
    static map<string, int> table;

    //輔助函式,用來判判斷src字串是否以tail串結尾
    bool stringEndsWith(const string& src, const string& tail)
    {
        if(src.size() < tail.size())
            return false;

        string tmp = src.substr(src.size() - tail.size(), tail.size());
        return (0==tmp.compare(0,tail.size(),tail));
    }
public:
    //虛方法:中文數字到數字的轉換
    virtual void interpret(Context& context)
    {
        if(context.getStatement().length() == 0)
            return;

        map<string, int>::iterator iter = table.begin();

        while (iter != table.end())
        {
            string& statement = context.getStatement();
            string tail = iter->first + getPostfix();
            //從低位往高位分析(如九千三百零五,從右向左分析)
            if(stringEndsWith(statement,tail))
            {
                context.setData(context.getData() + iter->second * multiplier());

                //注意,string是ASCII編碼,每個中文字元的長度為2
                context.setStatement(statement.substr(0, statement.length()-2 - getPostfix().length()));
            }

            if(stringEndsWith(statement,""))
            {
                //”零“則直接跳過
                context.setStatement(statement.substr(0, statement.length()-2));
            }
            ++iter;
        }
    }

    //表示式的字尾是以什麼表示的(十、百...)
    virtual string getPostfix() = 0;

    //表示式的數量級
    virtual int multiplier() = 0;

    virtual ~Expression(){};
};

//對映表,儲存中文數字與羅馬數字的對映
static map<string, int>::value_type init_table[] =
{
       map<string, int>::value_type("",1),
       map<string, int>::value_type("",2),
       map<string, int>::value_type("",3),
       map<string, int>::value_type("",4),
       map<string, int>::value_type("",5),
       map<string, int>::value_type("",6),
       map<string, int>::value_type("",7),
       map<string, int>::value_type("",8),
       map<string, int>::value_type("",9)
};

map<string,int> Expression::table(init_table,init_table + 9);

//個位數直譯器(終結符表示式)
class GeExpression : public Expression
{
public:
    string getPostfix()
    {
        return "";
    }

    int multiplier()
    {
        return 1;
    }
};

//十位數直譯器(終結符表示式)
class ShiExpression  : public Expression
{
public:
    string getPostfix()
    {
        return "";
    }

    int multiplier()
    {
        return 10;
    }
};

//百位數直譯器(終結符表示式)
class BaiExpression  : public Expression
{
public:
    string getPostfix()
    {
        return "";
    }

    int multiplier()
    {
        return 100;
    }
};


//千位數直譯器(終結符表示式)
class QianExpression : public Expression
{
public:
    string getPostfix()
    {
        return "";
    }

    int multiplier()
    {
        return 1000;
    }
};

//萬位數直譯器(非終結符表示式)
class WanExpression : public Expression
{
public:
    string getPostfix()
    {
        return "";
    }

    int multiplier()
    {
        return 10000;
    }

    void interpret(Context& context)
    {
        if(context.getStatement().length() == 0)
            return ;

        if (stringEndsWith(context.getStatement(),getPostfix()))
        {
            list<Expression*> exps;
            exps.clear();
            exps.push_back(new GeExpression());
            exps.push_back(new ShiExpression());
            exps.push_back(new BaiExpression());
            exps.push_back(new QianExpression());

            int temp = context.getData();
            string& sm = context.getStatement();
            context.setData(0);
            //string類中每個中文長度為2.
            context.setStatement(sm.substr(0, sm.length()-2));

            list<Expression*>::iterator iter = exps.begin();

            while (iter != exps.end())
            {
                (*iter)->interpret(context);
                ++iter;
            }

            context.setData(temp + multiplier()* context.getData());

            iter = exps.begin();
            while (iter != exps.end())
            {
                delete (*iter);
                ++iter;
            }
            exps.clear();
        }
    }
};

//轉換器
class Convertor
{
private:
    Context context;
    string chineseNum;
    int result;
    list<Expression*> exps;

    void reset()
    {
        context.setStatement(chineseNum);
        context.setData(0);

        list<Expression*>::iterator iter = exps.begin();

        while (iter != exps.end())
        {
            delete (*iter);
            ++iter;
        }

        exps.clear();
    }

public:
    Convertor(const string chineseNum):context(chineseNum)
    {
        this ->chineseNum = chineseNum;
        result = 0;
    }

   void convert()
   {
        reset();

        exps.push_back(new GeExpression());
        exps.push_back(new ShiExpression());
        exps.push_back(new BaiExpression());
        exps.push_back(new QianExpression());
        exps.push_back(new WanExpression());

        list<Expression*>::iterator iter = exps.begin();

        while (iter != exps.end())
        {
            (*iter)->interpret(context);
            ++iter;
        }
        result = context.getData();
   }

   
            
           

相關推薦

設計模式C++實現20——直譯器模式

1. 直譯器模式(Interpreter Pattern)的定義 (1)定義   給定一個語言,定義它的文法的一種表示,並定義一個直譯器,這個直譯器使用該表示來解釋語言中的句子。   ①文法:即語法規則。在直譯器模式中每一個語法都將對應一個直譯器物件,用來處理相應的

設計模式C++實現2——策略模式

       軟體領域中的設計模式為開發人員提供了一種使用專家設計經驗的有效途徑。設計模式中運用了面向物件程式語言的重要特性:封裝、繼承、多型,真正領悟設計模式的精髓是可能一個漫長的過程,需要大量實踐經驗的積累。最近看設計模式的書,對於每個模式,用C++寫了個小例子,加深一

設計模式C++實現3——介面卡模式

        軟體領域中的設計模式為開發人員提供了一種使用專家設計經驗的有效途徑。設計模式中運用了面向物件程式語言的重要特性:封裝、繼承、多型,真正領悟設計模式的精髓是可能一個漫長的過程,需要大量實踐經驗的積累。最近看設計模式的書,對於每個模式,用C++寫了個小例子,加深

設計模式C++實現12——備忘錄模式

       軟體領域中的設計模式為開發人員提供了一種使用專家設計經驗的有效途徑。設計模式中運用了面向物件程式語言的重要特性:封裝、繼承、多型,真正領悟設計模式的精髓是可能一個漫長的過程,需要大量實踐經驗的積累。最近看設計模式的書,對於每個模式,用C++寫了個小例子,加深一下理解。主要參考《大話設計模式》

設計模式C++實現1——工廠模式

       軟體領域中的設計模式為開發人員提供了一種使用專家設計經驗的有效途徑。設計模式中運用了面向物件程式語言的重要特性:封裝、繼承、多型,真正領悟設計模式的精髓是可能一個漫長的過程,需要大量實踐經驗的積累。最近看設計模式的書,對於每個模式,用C++寫了個小例子,加深一下理解。主要參考《大話設計模式》

設計模式C++實現11——裝飾模式

          軟體領域中的設計模式為開發人員提供了一種使用專家設計經驗的有效途徑。設計模式中運用了面向物件程式語言的重要特性:封裝、繼承、多型,真正領悟設計模式的精髓是可能一個漫長的過程,需要大量實踐經驗的積累。最近看設計模式的書,對於每個模式,用C++寫了個小例子,

設計模式C++實現5——原型模式、模板方法模式

       軟體領域中的設計模式為開發人員提供了一種使用專家設計經驗的有效途徑。設計模式中運用了面向物件程式語言的重要特性:封裝、繼承、多型,真正領悟設計模式的精髓是可能一個漫長的過程,需要大量實踐經驗的積累。最近看設計模式的書,對於每個模式,用C++寫了個小例子,加深一

設計模式C++實現9——享元模式

        軟體領域中的設計模式為開發人員提供了一種使用專家設計經驗的有效途徑。設計模式中運用了面向物件程式語言的重要特性:封裝、繼承、多型,真正領悟設計模式的精髓是可能一個漫長的過程,需要大量實踐經驗的積累。最近看設計模式的書,對於每個模式,用C++寫了個小例子,加深一下理解。主要參考《大話設計模式》

設計模式 c++版9——原型模式

定義:用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。 示例一:個性化電子賬單 1. 需求說明: 銀行傳送電子賬單的郵件一般是有要求的:①個性化服務:發過去的郵件上總有一些個人資訊,比如姓氏等。②遞送成功率:若大批量地傳送郵件會被收房郵件伺服器誤認為是

設計模式 c++版13——策略模式

定義: 定義一組演算法,將每個演算法都封裝起來,並且使他們之間可以互換 示例一:策略模式(通用版) 1. 類圖18-3 2. 類圖說明 策略模式使用的就是面向物件的繼承和多型機制 Context 封裝角色。也叫上下文角色,起承上啟下的封裝作用,遮蔽高層模組對策

設計模式 c++版18——門面模式

定義: 要求一個子系統的外部與其內部的通訊必須通過一個統一的物件進行。門面模式提供一個高層次的介面,使得子系統更易於使用(門面模式也叫做外觀模式)。 示例一:門面模式(通用版) 1. 類圖23-4 2. 類圖說明 Subsystem Classes 是子系統所有

六種常用的設計模式java實現模板模式

模板模式,估計大家應該都接觸過,比如說,在完成某個業務邏輯前,需要處理一點事,在完成後也要處理一點事,這樣就可以把不一樣的地方給抽象出來,然後公共的地方都是一樣的,這樣的場景就會用到模板模式。 一、基本概念 模板方法模式是類的行為模式。準備一個抽象類,將部分

設計模式 c++版19—— 狀態模式

定義: 當一個物件內在狀態改變時允許其改變行為,這個物件看起來像改變了其類。(狀態的變更引起了行為的變更,從外部看起來好像這個物件對應的類發生了改變一樣) 示例一:狀態模式(通用版) 1. 類圖 26-5 2. 類圖說明 State 抽象狀態角色 介面或抽象類,

設計模式C++實現:包裝器外觀模式Wrapper Facade

包裝器外觀模式(Wrapper Facade)把現有的非面向物件的API所提供的函式和資料,封裝在更加簡潔的、健壯的、可移植的、可維護的和內聚的面向物件的類介面中。 一般通過兩種方式實現跨平臺: 1

《Head First 設計模式》例子的C++實現2 觀察者模式

最近在學習設計模式,用的是 《Head First 設計模式》這本書。感覺這本書寫的還是很不錯的,深入淺出的介紹了各種常用的設計模式。唯一有點不方便的地方是這本書的例子全都是用的 Java 來實現的。而我主要是用 C++。所以就動手將書上的程式碼用 C++ 來實

設計模式 c++版5——抽象工廠模式

定義:為建立一組相關或相互依賴的物件提供一個介面,而且無需指定它們的具體類 示例一:女媧造人擴充套件 造出來黑、白、黃種人,分別有性別分類 類圖說明:一個介面,多個抽象類,N個實現類,每個人種都是一個抽象類,性別是在各個實現類中實現的。 1. 結構說明: HumanF

設計模式 c++版7——建造者模式

定義:即生成器模式,將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。 示例一:汽車模型擴充套件,根據不同的產品,執行任務的順序不同 1. 類圖說明: 在CarModel 中定義了一個 setSequence 方法,車輛模型的動作如何排布在 Ar

設計模式直譯器模式

化繁為簡的翻譯機——直譯器模式 直譯器模式的定義 給定一個語言,定義它的文法的一種表示,並定義一個直譯器,該直譯器使用該表示來解釋語言中的句子。其實就是自己定義一個規則,將一個語言解釋清楚。 直譯器模式的使用場景 如果某個簡單的語言需要解釋執行而且可以將該語

設計模式與XML組合模式、橋接模式和介面卡模式(C++)

一、實驗目的及要求 1、掌握結構型模式的概念。 2、掌握介面卡模式、橋接模式、組合模式、裝飾器模式、外觀模式、享元模式、代理模式的構造方式及使用情景。 二、實驗裝置(環境) 1、   軟體需求: Dev-Cpp5.4, Rat

設計模式與XML策略模式(C++)

一、實驗目的及要求 1、掌握行為型模式的概念。 2、掌握備忘錄模式、觀察者模式、狀態模式、策略模式、模板模式、訪問者模式的構造方式及使用情景。 二、實驗裝置(環境)    1、   軟體需求: Dev-Cpp5.4, Rational Rose / Microsoft