思考解释器模式
(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 CContext{
private:
string strStatement;
int iData;
public:
void SetStatement(string statement);
string& GetStatement();
void SetData(int data);
int GetData();
};
//抽象类:解释器
class CNumExp{
protected://数据字典:保存中文数字一到九
static map<string, int> mpTable;
bool StringEndsWith(const string& src, const string& tail);
public:
//虚方法:中文数字到数字的转换
virtual void Interpreter(CContext& context);
virtual string Postfix() = 0; //表达式的后缀是以什么表示的(十、百...)
virtual int Multiplier() = 0; //表达式的数量级
virtual ~CNumExp();
};
//个位数解释器(终结符表达式)
class CExp1 : public CNumExp{
public:
string Postfix();
int Multiplier();
};
//十位数解释器(终结符表达式)
class CExp10 : public CNumExp{
public:
string Postfix();
int Multiplier();
};
//百位数解释器(终结符表达式)
class CExp100 : public CNumExp{
public:
string Postfix();
int Multiplier();
};
//千位数解释器(终结符表达式)
class CExp1000 : public CNumExp{
public:
string Postfix();
int Multiplier();
};
//万位数解释器(非终结符表达式)
class CExp10000 : public CNumExp{
public:
string Postfix();
int Multiplier();
void Interpreter(CContext& context);
};
//转换器
class CConvertor{
private:
CContext oContext;
string strChinese;
int iResult;
list<CNumExp*> lstExp;
void Reset();
public:
CConvertor();
~CConvertor();
int Convert(string chinese);
};
//实现文件
//字符串上下文信息:保存没有处理的字符串信息
void CContext::SetStatement(string statement){strStatement = statement;}
string& CContext::GetStatement(){return strStatement;}
void CContext::SetData(int data){iData = data;}
int CContext::GetData(){return iData;}
//抽象类:解释器
bool CNumExp::StringEndsWith(const string& src, const string& tail){
if(src.size() < tail.size()) return false;
string strTmp = src.substr(src.size() - tail.size(), tail.size());
return (strTmp.compare(0, tail.size(), tail) == 0);
}
//虚方法:中文数字到数字的转换
void CNumExp::Interpreter(CContext& context){
if(context.GetStatement().length() == 0) return;
for(map<string, int>::iterator it = mpTable.begin(); it != mpTable.end(); it++){
string& strStatement = context.GetStatement();
string strTail = it->first + Postfix();
//从低位往高位分析(如九千三百零五,从右向左分析)
if(StringEndsWith(strStatement, strTail)){
context.SetData(context.GetData() + it->second * Multiplier());
context.SetStatement(strStatement.substr(0, strStatement.length()-2-Postfix().length()));
}
if(StringEndsWith(strStatement, "零")){//”零“则直接跳过
context.SetStatement(strStatement.substr(0, strStatement.length()-2));
}
}
}
CNumExp::~CNumExp(){}
//映射表,保存中文数字与罗马数字的映射
static map<string, int>::value_type init_data[] =
{
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> CNumExp::mpTable(init_data, init_data+9);
//个位数解释器(终结符表达式)
string CExp1::Postfix(){return "";}
int CExp1::Multiplier(){return 1;}
//十位数解释器(终结符表达式)
string CExp10::Postfix(){return "十";}
int CExp10::Multiplier(){return 10;}
//百位数解释器(终结符表达式)
string CExp100::Postfix(){return "百";}
int CExp100::Multiplier(){return 100;}
//千位数解释器(终结符表达式)
string CExp1000::Postfix(){return "千";}
int CExp1000::Multiplier(){return 1000;}
//万位数解释器(非终结符表达式)
string CExp10000::Postfix(){return "万";}
int CExp10000::Multiplier(){return 10000;}
void CExp10000::Interpreter(CContext& context){
if(context.GetStatement().length() == 0) return;
if(StringEndsWith(context.GetStatement(), Postfix())){
list<CNumExp*> lstExp; lstExp.clear();
lstExp.push_back(new CExp1());
lstExp.push_back(new CExp10());
lstExp.push_back(new CExp100());
lstExp.push_back(new CExp1000());
int iTmp = context.GetData();
string& strStatement = context.GetStatement();
context.SetData(0);
context.SetStatement(strStatement.substr(0, strStatement.length()-2));
for(list<CNumExp*>::iterator it = lstExp.begin(); it != lstExp.end(); it++){
(*it)->Interpreter(context);
}
context.SetData(iTmp + Multiplier()*context.GetData());
cout << "Before deleting... size : " << lstExp.size() << endl;
for(list<CNumExp*>::iterator it = lstExp.begin(); it != lstExp.end(); ){
CNumExp* pExp = (*it); delete pExp; it = lstExp.erase(it);
}
cout << "After deleted... size : " << lstExp.size() << endl;
}
}
//转换器
void CConvertor::Reset(){
oContext.SetStatement(strChinese);
oContext.SetData(iResult);
for(list<CNumExp*>::iterator it = lstExp.begin(); it != lstExp.end(); ){
CNumExp* pExp = (*it); delete pExp; it = lstExp.erase(it);
}
}
CConvertor::CConvertor(){ strChinese = ""; iResult = 0;}
CConvertor::~CConvertor(){Reset();}
int CConvertor::Convert(string chinese){
strChinese = chinese; iResult = 0;
Reset();
lstExp.push_back(new CExp1());
lstExp.push_back(new CExp10());
lstExp.push_back(new CExp100());
lstExp.push_back(new CExp1000());
lstExp.push_back(new CExp10000());
for(list<CNumExp*>::iterator it = lstExp.begin(); it != lstExp.end(); it++){
CNumExp* pExp = (*it); pExp->Interpreter(oContext);
}
iResult = oContext.GetData();
return iResult;
}
//测试客户端
void main()
{
string strTst = "四百九十六万二千三百一十五";
CConvertor oConvertor;
cout << strTst << "---->" << oConvertor.Convert(strTst) << endl;
strTst = "九千零五万六千零七十二";
cout << strTst << "---->" << oConvertor.Convert(strTst) << endl;
}