亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

C++設計模式之解釋器模式

2020-01-26 15:16:18
字體:
來源:轉載
供稿:網友

前言

那日,閑的無聊,上了一個在線編程學習網站;最近那個在線編程學習網站很火?。恢?,蓋茨、扎克伯格等大人物都來宣傳了,思想是人人都應該學習編程;我一想就這算怎么回事啊?這要是在中國,還讓人活不?話題不扯開了,還是說我上了那個在線編程網站吧,首先是給你玩一個小游戲,激發你對編程的興趣。游戲是這樣的,網頁上有一個編輯框,屏幕上有一只小狗,比如你在編輯框中輸入這樣的句子:down run 10;按下回車,這個時候,你就看到屏幕上的小狗向下跑動了10個方格大小的長度;你再輸入up walk 5,按下回車,小狗就會向上走動5個方格大小的長度。確實是有點意思;但是,對于我這種已經不需要這種游戲來激起我學習興趣的人來說,我更喜歡的是去考慮它是如何實現的,如何將我輸入的一句話去控制小狗移動的。而這一切的一切都不得不說到今天總結的解釋器模式了。

解釋器模式

在GOF的《設計模式:可復用面向對象軟件的基礎》一書中對解釋器模式是這樣說的:給定一個語言,定義它的文法的一種表示,并定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。如果一種特定類型的問題發生的頻率足夠高,那么可能就值得將該問題的各個實例表述為一個簡單語言中的句子。這樣就可以構建一個解釋器,該解釋器通過解釋這些句子來解決該問題。

就如上面說的那個游戲,我輸入up walk 5,我必須按照:移動方向+移動方式+移動距離這種格式輸入我的指令,而這種格式的指令就是一種文法,只有按照了我定義的這種文法去輸入,才能控制屏幕上的小狗去移動。當然了,我輸入up walk 5,屏幕上的小狗肯定是聽不懂的,它不知道我輸入的是什么,這個時候需要怎么辦?我需要一個工具去將我輸入的內容翻譯成小狗能聽懂的東西,而這個工具就是定義中提到的解釋器,解釋器對我輸入的指令進行解釋,然后將解釋得到的指令發送給屏幕上的小狗,小狗聽懂了,就進行實際的移動。

我們在開發中經常用到的正則表達式也是這類問題的代表。我們有的時候需要去匹配電話號碼、身份證號;我們不用為了每一種匹配都寫一個特定的算法,我們可以為每一種匹配定義一種文法,然后去解釋這種文法定義的句子就ok了。

文法規則和抽象語法樹

上面對于解釋器模式的定義中,提及到了一個詞:文法。在使用代碼實現解釋器模式之前,是非常有必要去學習一下文法的概念,以及如何表示一個語言的文法規則。再拿上面的游戲這個例子進行說明,我可以定義以下五條文法:

復制代碼 代碼如下:

expression ::= direction action distance | composite //表達式
composite ::= expression 'and' expression //復合表達式
direction ::= 'up' | 'down' | 'left' | 'right' //移動方向
action ::= 'move' | 'walk' //移動方式
distance ::= an integer //移動距離

上面的5條文法規則,對應5個語言單位,這些語言單位可以分為兩大類:一類為終結符(也叫做終結符表達式),例如上面的direction、action和distance,它們是語言的最小組成單位,不能再進行拆分;另一類為非終結符(也叫做非終結符表達式),例如上面的expression和composite,它們都是一個完整的句子,包含一系列終結符或非終結符。

我們就是根據上面定義的一些文法可以構成更多復雜的語句,計算機程序將根據這些語句進行某種操作;而我們這里列出的文法,計算機是無法直接看懂的,所以,我們需要對我們定義的文法進行解釋;就好比,我們編寫的C++代碼,計算機是看不懂的,我們需要進行編譯一樣。解釋器模式,就提供一種模式去給計算機解釋我們定義的文法,讓計算機根據我們的文法去進行工作。

在文法規則定義中可以使用一些符號來表示不同的含義,如使用“|”表示或,使用“{”和“}”表示組合,使用“*”表示出現0次或多次等,其中使用頻率最高的符號是表示“或”關系的“|”,如文法規則“bool Value ::= 0 | 1”表示終結符表達式bool Value的取值可以為0或者1。

除了使用文法規則來定義一個語言,在解釋器模式中還可以通過一種稱之為抽象語法樹的圖形方式來直觀地表示語言的構成,每一棵語法樹對應一個語言實例,對于上面的游戲文法規則,可以通過以下的抽象語法樹來進行表示:

在抽象語法樹種,可以通過終結符表達式和非終結符表達式組成復雜的語句,每個文法規則的語言實例都可以表示為一個抽象語法樹,就是說每一條具體的語句都可以用類似上圖所示的抽象語法樹來表示,在圖中終結符表達式類的實例作為樹的葉子節點,而非終結符表達式類的實例作為非葉子節點。抽象語法樹描述了如何構成一個復雜的句子。

UML類圖

AbstractExpression:聲明一個抽象的解釋操作,這個接口被抽象語法樹中所有的節點所共享;
TernimalExpression:一個句子中的每個終結符需要該類的一個實例,它實現與文法中的終結符相關聯的解釋操作;
NonternimalExpression:

1.對于文法中的每一條規則都需要一個NonternimalExpression類;
2.為文法中的的每個符號都維護一個AbstractExpression類型的實例變量;
3.為文法中的非終結符實現解釋操作,在實現時,一般要遞歸地調用表示文法符號的那些對象的解釋操作;

Context:包含解釋器之外的一些全局信息;
Client:構建一個需要進行解釋操作的文法句子,然后調用解釋操作進行解釋。

實際進行解釋時,按照以下時序進行的:

1.Client構建一個句子,它是NonterminalExpression和TerminalExpression的實例的一個抽象語法樹,然后初始化上下文并調用解釋操作;
2.每一非終結符表達式節點定義相應子表達式的解釋操作。而各終結符表達式的解釋操作構成了遞歸的基礎;
3.每一節點的解釋操作用作用上下文來存儲和訪問解釋器的狀態。

使用場合

在以下情況下可以考慮使用解釋器模式:

1.可以將一個需要解釋執行的語言中的句子表示為一個抽象語法樹;
2.一些重復出現的問題可以用一種簡單的語言來進行表達;
3.一個語言的文法較為簡單;
4.執行效率不是關鍵問題?!咀ⅲ焊咝У慕忉屍魍ǔ2皇峭ㄟ^直接解釋抽象語法樹來實現的,而是需要將它們轉換成其他形式,使用解釋器模式的執行效率并不高。】

代碼實現

我們這里用代碼來實現上面的游戲,只不過不是控制小狗在屏幕上移動了,而是將對應的控制指令翻譯成漢語進行表示,這和翻譯成控制小狗移動的指令的原理是一樣的。比如現在有指令:down run 10;那么,經過解釋器模式得到的結果為:向下跑動10。

復制代碼 代碼如下:

#include <iostream>
#include <vector>
using namespace std;
 
#define MAX_SIZE 256
#define SAFE_DELETE(p) if (p) { delete p; p = NULL; }
 
const wchar_t *const DOWN = L"down";
const wchar_t *const UP = L"up";
const wchar_t *const LEFT = L"left";
const wchar_t *const RIGHT = L"right";
 
const wchar_t *const MOVE = L"move";
const wchar_t *const WALK = L"walk";
 
class AbstractNode
{
public:
     virtual wchar_t *Interpret() = 0;
};
 
class AndNode : public AbstractNode
{
public:
     AndNode(AbstractNode *left, AbstractNode *right) : m_pLeft(left), m_pRight(right){}
 
     wchar_t *Interpret()
     {
          wchar_t *pResult = new wchar_t[MAX_SIZE];
          memset(pResult, 0, MAX_SIZE * sizeof(wchar_t));
 
          wchar_t *pLeft = m_pLeft->Interpret();
          wchar_t *pRight = m_pRight->Interpret();
          wcscat_s(pResult, MAX_SIZE, pLeft);
          wcscat_s(pResult, MAX_SIZE, pRight);
 
          SAFE_DELETE(pLeft);
          SAFE_DELETE(m_pRight);
 
          return pResult;
     }
 
private:
     AbstractNode *m_pLeft;
     AbstractNode *m_pRight;
};
 
class SentenceNode : public AbstractNode
{
public:
     SentenceNode(AbstractNode *direction, AbstractNode *action, AbstractNode *distance) :
          m_pDirection(direction), m_pAction(action), m_pDistance(distance){}
 
     wchar_t *Interpret()
     {
          wchar_t *pResult = new wchar_t[MAX_SIZE];
          memset(pResult, 0, MAX_SIZE * sizeof(wchar_t));
 
          wchar_t *pDirection = m_pDirection->Interpret();
          wchar_t *pAction = m_pAction->Interpret();
          wchar_t *pDistance = m_pDistance->Interpret();
          wcscat_s(pResult, MAX_SIZE, pDirection);
          wcscat_s(pResult, MAX_SIZE, pAction);
          wcscat_s(pResult, MAX_SIZE, pDistance);
 
          SAFE_DELETE(pDirection);
          SAFE_DELETE(pAction);
          SAFE_DELETE(pDistance);
 
          return pResult;
     }
 
private:
     AbstractNode *m_pDirection;
     AbstractNode *m_pAction;
     AbstractNode *m_pDistance;
};
 
class DirectionNode : public AbstractNode
{
public:
     DirectionNode(wchar_t *direction) : m_pDirection(direction){}
 
     wchar_t *Interpret()
     {
          wchar_t *pResult = new wchar_t[MAX_SIZE];
          memset(pResult, 0, MAX_SIZE * sizeof(wchar_t));
 
          if (!_wcsicmp(m_pDirection, DOWN))
          {
               wcscat_s(pResult, MAX_SIZE, L"向下");
          }
          else if (!_wcsicmp(m_pDirection, UP))
          {
               wcscat_s(pResult, MAX_SIZE, L"向上");
          }
          else if (!_wcsicmp(m_pDirection, LEFT))
          {
               wcscat_s(pResult, MAX_SIZE, L"向左");
          }
          else if (!_wcsicmp(m_pDirection, RIGHT))
          {
               wcscat_s(pResult, MAX_SIZE, L"向右");
          }
          else
          {
               wcscat_s(pResult, MAX_SIZE, L"無效指令");
          }
 
          SAFE_DELETE(m_pDirection);
          return pResult;
     }
 
private:
     wchar_t *m_pDirection;
};
 
class ActionNode : public AbstractNode
{
public:
     ActionNode(wchar_t *action) : m_pAction(action){}
 
     wchar_t *Interpret()
     {
          wchar_t *pResult = new wchar_t[MAX_SIZE];
          memset(pResult, 0, MAX_SIZE * sizeof(wchar_t));
 
          if (!_wcsicmp(m_pAction, MOVE))
          {
               wcscat_s(pResult, MAX_SIZE, L"移動");
          }
          else if (!_wcsicmp(m_pAction, WALK))
          {
               wcscat_s(pResult, MAX_SIZE, L"走動");
          }
          else
          {
               wcscat_s(pResult, MAX_SIZE, L"無效指令");
          }
 
          SAFE_DELETE(m_pAction);
          return pResult;
     }
 
private:
     wchar_t *m_pAction;
};
 
class DistanceNode : public AbstractNode
{
public:
     DistanceNode(wchar_t *distance) : m_pDistance(distance){}
 
     wchar_t *Interpret()
     {
          wchar_t *pResult = new wchar_t[MAX_SIZE];
          memset(pResult, 0, MAX_SIZE * sizeof(wchar_t));
 
          wcscat_s(pResult, MAX_SIZE, m_pDistance);
 
          SAFE_DELETE(m_pDistance);
          return pResult;
     }
 
private:
     wchar_t *m_pDistance;
};
 
class InstructionHandler
{
public:
     InstructionHandler(wchar_t *instruction) : m_pInstruction(instruction), m_pTree(NULL){}
 
     void Handle();
     void Output();
 
private:
     void SplitInstruction(wchar_t **&instruction, int &size);
 
     wchar_t *m_pInstruction;
     AbstractNode *m_pTree;
};
 
void InstructionHandler::Handle()
{
     AbstractNode *pLeft = NULL;
     AbstractNode *pRight = NULL;
     AbstractNode *pDirection = NULL;
     AbstractNode *pAction = NULL;
     AbstractNode *pDistance = NULL;
 
     vector<AbstractNode *> node; // Store the instruction expression
 
     // Split the instruction by " "
     wchar_t **InstructionArray = NULL;
     int size;
     SplitInstruction(InstructionArray, size);
     for (int i = 0; i < size; ++i)
     {
          if (!_wcsicmp(InstructionArray[i], L"and")) // The instruction is composited by two expressions
          {
               wchar_t *pDirectionStr = InstructionArray[++i];
               pDirection = new DirectionNode(pDirectionStr);
 
               wchar_t *pActionStr = InstructionArray[++i];
               pAction = new ActionNode(pActionStr);
 
               wchar_t *pDistanceStr = InstructionArray[++i];
               pDistance = new DistanceNode(pDistanceStr);
 
               pRight = new SentenceNode(pDirection, pAction, pDistance);
               node.push_back(new AndNode(pLeft, pRight));
          }
          else
          {
               wchar_t *pDirectionStr = InstructionArray[i];
               pDirection = new DirectionNode(pDirectionStr);
 
               wchar_t *pActionStr = InstructionArray[++i];
               pAction = new ActionNode(pActionStr);
 
               wchar_t *pDistanceStr = InstructionArray[++i];
               pDistance = new DistanceNode(pDistanceStr);
 
               pLeft = new SentenceNode(pDirection, pAction, pDistance);
               node.push_back(pLeft);
          }
     }
 
     m_pTree = node[node.size() - 1];
}
 
void InstructionHandler::Output()
{
     wchar_t *pResult = m_pTree->Interpret();
 
     setlocale(LC_ALL,"");
     wprintf_s(L"%s/n", pResult);
 
     SAFE_DELETE(pResult);
}
 
void InstructionHandler::SplitInstruction(wchar_t **&instruction, int &size)
{
     instruction = new wchar_t*[10];
     memset(instruction, 0, 10 * sizeof( wchar_t*));
 
     for (int i = 0; i < 10; ++i)
     {
          instruction[i] = new wchar_t[10];
          memset(instruction[i], 0, 10 * sizeof(wchar_t));
     }
 
     size = 0;
     int n = 0;
     while (*m_pInstruction != L'/0')
     {
          if (*m_pInstruction == L' ')
          {
               size++;
               m_pInstruction++;
               n = 0;
               continue;
          }
 
          instruction[size][n++] = *m_pInstruction++;
     }
     size++;
}
 
int main()
{
     wchar_t *pInstructionStr = L"up move 5 and down walk 10";
 
     InstructionHandler *pInstructionHandler = new InstructionHandler(pInstructionStr);
     pInstructionHandler->Handle();
     pInstructionHandler->Output();
 
     SAFE_DELETE(pInstructionHandler);
}

在上面的代碼中,我沒有用到Context類,一般Context類作為環境上下文類,用于存儲解釋器之外的一些全局信息,它通常作為參數被傳遞到所有表達式的解釋方法interpret中,可以在Context對象中存儲和訪問表達式解釋器的狀態,向表達式解釋器提供一些全局的、公共的數據,此外還可以在Context中增加一些所有表達式解釋器都共有的功能,減輕解釋器的職責。而我們在代碼中定義的一些常量,完全可以放入到Context類中,作為上下文的全局數據。

主要優點

1.易于改變和擴展文法。由于在解釋器模式中使用類來表示語言的文法規則,因此可以通過繼承等機制來改變或擴展文法;
2.每一條文法規則都可以表示為一個類,因此可以方便地實現一個簡單的語言;
3.實現文法較為容易;在抽象語法樹中每一個表達式節點類的實現方式都是相似的,這些類的代碼編寫都不會特別復雜;
4.增加新的解釋表達式較為方便。如果用戶需要增加新的解釋表達式只需要對應增加一個新的終結符表達式類或非終結符表達式類,原有表達式類代碼無須修改,符合“開閉原則”。

主要缺點

1.對于復雜文法難以維護;在解釋器模式中,每一條規則至少需要定義一個類,因此如果一個語言包含太多文法規則,類的個數將會急劇增加,導致系統難以管理和維護,此時可以考慮使用語法分析程序等方式來取代解釋器模式;
2.執行效率低;由于在解釋器模式中使用了大量的循環和遞歸調用,因此在解釋較為復雜的句子時其速度很慢,而且代碼的調試過程也很麻煩。

總結

解釋器模式在實際的系統開發中使用的非常少,因為它會引起效率、性能以及維護方面的問題,并且難度較大,一般在一些大中型的框架型項目中能夠找到它的身影。而現在又有很多的開源庫提供了對實際需要的支持,所以,我們在實際開發中沒有必要再去重復造輪子,能夠理解了解釋器模式就好了。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品久久久久久久av大片| 欧美午夜激情视频| 韩剧1988免费观看全集| 亚洲国产精品人人爽夜夜爽| 久久91亚洲精品中文字幕| 欧美激情精品久久久久久变态| 亚洲第一区中文99精品| 欧美日韩国产丝袜另类| 欧美日韩免费区域视频在线观看| 日本精品视频网站| 福利一区福利二区微拍刺激| 国产日韩欧美综合| 欧美视频在线免费看| 久久中文久久字幕| 高清视频欧美一级| 蜜臀久久99精品久久久久久宅男| 久久久精品电影| 97成人精品区在线播放| 成人激情黄色网| 一本大道亚洲视频| 亚洲人成在线观看| 国产免费一区二区三区在线能观看| 久久精品中文字幕免费mv| 精品国产一区二区三区久久狼黑人| 久久久久久伊人| 最好看的2019的中文字幕视频| 久久久在线观看| 97avcom| 国产亚洲一级高清| 亚洲精品av在线播放| 亚洲爱爱爱爱爱| 92版电视剧仙鹤神针在线观看| 亚洲国产精品专区久久| 最近2019中文字幕一页二页| 久久久免费观看视频| 日韩欧美视频一区二区三区| 亚洲精品久久久一区二区三区| 国产欧美一区二区三区久久| 国产综合视频在线观看| 欧美性生交xxxxxdddd| 精品久久久久久久久久ntr影视| 日韩欧美高清视频| 日本高清+成人网在线观看| 欧美日韩国产综合新一区| 久久精品久久久久久国产 免费| 日韩美女视频中文字幕| 欧美极品欧美精品欧美视频| 亚洲精品电影网| 在线观看国产精品日韩av| 日韩一二三在线视频播| 久久久av一区| 精品视频在线播放色网色视频| 日韩欧美国产成人| 久久精品国产成人精品| 亚洲精品自拍第一页| 久久久精品国产一区二区| 久久久亚洲欧洲日产国码aⅴ| 91产国在线观看动作片喷水| 韩国美女主播一区| 国产精品丝袜久久久久久高清| 成人免费看片视频| 国产日韩一区在线| 中文字幕精品—区二区| 亚洲精品在线视频| 久久久精品久久久| 欧美肥婆姓交大片| 亚洲福利影片在线| 亚洲欧美国产日韩中文字幕| 91久久久久久久一区二区| 国产成人一区二区| 热久久视久久精品18亚洲精品| 91精品国产91久久久久久不卡| 亚洲精品国产精品自产a区红杏吧| 美女av一区二区三区| 亚洲成人久久一区| 亚洲人高潮女人毛茸茸| 国产一级揄自揄精品视频| 国产精品免费一区二区三区都可以| 精品视频中文字幕| 欧美裸体xxxx极品少妇软件| 91理论片午午论夜理片久久| 成人h视频在线| 日韩精品在线观看一区二区| 欧美一级片久久久久久久| 国模精品视频一区二区三区| 最近中文字幕日韩精品| 精品偷拍各种wc美女嘘嘘| 中文字幕亚洲激情| 日本精品一区二区三区在线播放视频| 97视频在线免费观看| 91国产精品视频在线| 国产精品久久激情| 国产精品扒开腿爽爽爽视频| 亚洲精品国产精品国产自| 自拍视频国产精品| 一区二区国产精品视频| 国产成人精品av在线| 国产亚洲欧美日韩精品| 日韩在线观看网站| 国产精品海角社区在线观看| 国产精品影片在线观看| 黄色精品在线看| 一区二区三区视频免费| 国产精品观看在线亚洲人成网| 成人国产精品久久久久久亚洲| 在线不卡国产精品| 亚洲欧美制服丝袜| 欧美激情a在线| 亚洲精品视频在线观看视频| 国产午夜一区二区| 中文字幕欧美日韩va免费视频| 国外成人性视频| 欧美性xxxxx极品娇小| 91免费欧美精品| 久久久久久久久久久国产| 日韩免费观看av| 亚洲国产精品网站| 日本91av在线播放| 日韩在线免费观看视频| 国产丝袜精品第一页| 欧美日韩日本国产| 日韩av片免费在线观看| 91精品视频在线播放| 在线看福利67194| 日韩美女av在线免费观看| 亚洲免费人成在线视频观看| 91精品国产亚洲| 亚洲福利在线视频| 亚洲精品国产精品国自产观看浪潮| 国产精品中文久久久久久久| 高清视频欧美一级| 欧美日韩免费网站| 欧美理论在线观看| 成年无码av片在线| 久久精品国产综合| 亚洲第一区第二区| 91精品国产综合久久久久久久久| 欧洲中文字幕国产精品| 亚洲欧洲第一视频| 97久久超碰福利国产精品…| 国产欧美日韩中文| 国产综合久久久久| 久久久国产精品视频| 亚洲国产精彩中文乱码av在线播放| 2020久久国产精品| 日韩视频―中文字幕| 亚洲欧美日韩在线一区| 98精品在线视频| 丰满岳妇乱一区二区三区| 欧美专区第一页| 精品国偷自产在线视频99| 亚洲丝袜一区在线| 日韩在线观看免费高清| 国产免费一区二区三区在线观看| 日韩中文字幕国产精品| 欧美日韩国产色视频| 国产婷婷色综合av蜜臀av| 成人免费黄色网| 亚洲成人国产精品| 国产精品视频区| 久久久久久久久久久亚洲| 亚洲精品国产精品国自产观看浪潮| 亚洲最大av网站| 日韩免费av一区二区|