解釋器模式(Interpreter Pattern)
定義
給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
- AbstractExpression(抽象表達式):聲明一個抽象的解釋操作,這個接口爲抽象語法樹中所有的節點所共享。
- TerminalExpression(終結符表達式):實現與文法中的終結符相關聯的解釋操作。實現抽象表達式中所要求的接口,主要是一個interpreter()方法。文法中每一個終結符都有一個具體終結表達式與之相對應。
- NonterminalExpression(非終結符表達式):爲文法中的非終結符實現解釋操作。
- Context:包含解釋器之外的一些全局信息。
常用場景
- 當一個語言需要解釋執行,並可以將該語言中的句子表示爲一個抽象語法樹的時候,可以考慮使用解釋器模式(如XML文檔解釋、正則表達式等領域)
- 一些重複出現的問題可以用一種簡單的語言來進行表達。
- 一個語言的文法較爲簡單.
- 當執行效率不是關鍵和主要關心的問題時可考慮解釋器模式(注:高效的解釋器通常不是通過直接解釋抽象語法樹來實現的,而是需要將它們轉換成其他形式,使用解釋器模式的執行效率並不高。)
優缺點
優點:
- 易於實現文法:在解釋器模式中,一條語法規則用一個解釋器對象來解釋執行。對於解釋器的實現來講,功能就變得比較簡單,只需要考慮這一條語法規則的實現就可以了,其他的都不用管。
- 易於擴展新的語法。由於解釋器採用類來描述語法規則,因此可以通過繼承等機制創建相應的解釋器對象,在創建抽象語法樹的時候使用這個新的解釋器對象就可以了。
缺點:
- 執行效率較低。由於在解釋器模式中使用了大量的循環和遞歸調用,因此在解釋較爲複雜的句子時其速度很慢,而且代碼的調試過程也比較麻煩。
- 對於複雜文法難以維護。在解釋器模式中,每一條規則至少需要定義一個類,因此如果一個語言包含太多文法規則,類的個數將會急劇增加,導致系統難以管理和維護,此時可以考慮使用語法分析程序等方式來取代解釋器模式。
C++實現
舉例:中文數字轉阿拉伯數字
在很多場景下,我們需要將中文數字轉爲阿拉伯數字,此時可以考慮解釋器模式
類圖:
代碼:
/*!
*@file Interpreter.h
*@brief 解釋器模式
*/
#ifndef INPERPRETER_H
#define INPERPRETER_H
//行爲型模式:解釋器模式
//場景:中文數字轉阿拉伯數字
/*
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();
}
int getRoman()
{
return result;
}
void setChineseNum(const string& chineseNum)
{
this ->chineseNum = chineseNum;
}
~Convertor()
{
reset();
}
};
void InterpreterTest()
{
string chineseNum = "四百九十六萬二千三百一十五";
Convertor conv(chineseNum);
conv.convert();
cout << chineseNum << " -> " << conv.getRoman() << endl;
chineseNum = "九千零五萬六千零七十二";
conv.setChineseNum(chineseNum);
conv.convert();
cout << chineseNum << " -> " << conv.getRoman() << endl;
}
#endif // INPERPRETER_H
參考:https://blog.csdn.net/zang141588761/article/details/53483645