第27章解释器模式

一 概念

  • 解释器模式,给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
  • 解释器要解决的问题是,如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子,这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

二 UML图

  • Context 包含解释器之外的一些全局信息,对全局的一些内容进行描述
  • AbstractExpression 抽象表达式,声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。
  • TerminalExpression 终结符表达式,实现与文法中的终结符相关联的解释操作。
  • NonterminalExpression 非终结符表达式,为文法中的非终结符实现解释操作,对文法中每一条规则R1,R2,。。。Rn都需要一个具体的非终结符表达式类。

三 C++代码实现

#include "pch.h"
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;

template <typename elemType>
elemType stringToNum(const string& str)
{
	istringstream iss(str);
	elemType num;
	iss >> num;
	return num;
}

//包含解释器之外的一些全局信息
class PlayContext
{
public:
	void SetPlayContext(const string text)
	{
		this->str_text = text;
	}
	string GetPlayContext() const
	{
		return this->str_text;
	}
private:
	string str_text;
};
//表达式类 AbstractExpression
//抽象表达式,声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享
class Expression
{
public:
	virtual void Interpret(PlayContext* context)
	{
		string buf;
		string s2;
		if (context->GetPlayContext().size() == 0)
		{
			return;
		}
		else
		{
			vector<string> vec;
			stringstream ss(context->GetPlayContext());
			while (ss >> buf)
			{
				vec.push_back(buf);
			}

			//这里是C++的字符串处理
			string playKey = vec[0];
			double playValue = stringToNum<double>(vec[1]);
		    this->Excute(playKey, playValue);

			vec.erase(vec.begin(), vec.begin() + 2);
			vector<string>::iterator it;
			for (it = vec.begin(); it != vec.end(); it++)
			{
				s2 += *it;
				if (it != vec.end() - 1)
					s2 += " ";
			}
			context->SetPlayContext(s2);
		}
	}
	virtual void Excute(string key, double value) = 0;
};

//音符类
//TerminalExpression 终结符表达式,实现与文法中的终结符相关联的解释操作
class Note : public Expression
{
	void Excute(string key, double value) override
	{
		string note = "";
		switch (key[0])
		{
		case 'C':
			note = "1";
			break;
		case 'D':
			note = "2";
			break;
		case 'E':
			note = "3";
			break;
		case 'F':
			note = "4";
			break;
		case 'G':
			note = "5";
			break;
		case 'A':
			note = "6";
			break;
		case 'B':
			note = "7";
			break;
		default:
			break;
		}
		cout << note << " ";
	}
};

class Scale : public Expression
{
public:
	void Excute(string key, double value) override
	{
		string scale = "";
		switch (static_cast<int>(value))
		{
		case 1:
			scale = "低音";
			break;
		case 2:
			scale = "中音";
			break;
		case 3:
			scale = "高音";
			break;
		default:
			break;
		}
		cout << scale << " ";
	}
};

int main()
{
	PlayContext* context = new PlayContext();
	cout << "上海滩: " << endl;
	context->SetPlayContext("O 2 E 0.5 G 0.5 A 3 E 0.5 G 0.5 D 3 E 0.5 G 0.5 A 0.5 O 3 C 1 O 2 A 0.5 G 1 C 0.5 E 0.5 D 3 ");
	while (context->GetPlayContext().size() > 0)
	{
		Expression* expression = nullptr;
		char c = context->GetPlayContext()[0];

		switch (c)
		{
		case 'O':
			//当首字段是O时,则表达式实例化为音阶
			expression = new Scale();
			break;
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'A':
		case 'B':
		case 'P':
			//当首字母是CDEFGAB,以及休止符P时,则实例化为音符
			expression = new Note();
			break;
		}
		expression->Interpret(context);
		delete expression;
	}
	system("pause");
}

补充的另外一个例子

  • 一个简单加减法运算器的实例
//一个简单加减法运算器的实例
#include "pch.h"
#include <iostream>
#include <string>
#include <vector>

using namespace std;

//抽象的表达式对象以及Context对象
//用于保存计算的中间结果以及当前执行的操作符
class Context
{
public:
	Context()
		:m_value(0), m_operator('\0')
	{}
	void SetOperator(char type)
	{
		this->m_operator = type;
	}
	char GetOperator() const
	{
		return this->m_operator;
	}
	void SetValue(int number)
	{
		this->m_value = number;
	}
	int GetValue() const
	{
		return this->m_value;
	}
private:
	int m_value;
	char m_operator;
};

//表示所有表达式的抽象接口
class IExpression
{
public:
	virtual void Eval(Context* p) = 0;
};
//拆分表达式的元素
class Operator : public IExpression
{ 
public:
	Operator(char op)
	{
		this->m_op = op;
	}
	void Eval(Context* pContext) override
	{
		pContext->SetOperator(this->m_op);
	}
private:
	char m_op;
};

//拆分操作数
class Operand : public IExpression
{
public:
	Operand(int number)
	{
		this->m_num = number;
	}
	void Eval(Context* pContext) override
	{
		switch (pContext->GetOperator())
		{
		case '\0':
			pContext->SetValue(this->m_num);
			break;
		case '+':
			pContext->SetValue(this->m_num + pContext->GetValue());
			break;
		case '-':
			pContext->SetValue(pContext->GetValue() - this->m_num);
			break;
		default:
			break;
		}
	}
private:
	int m_num;
};

class Calculator
{
public:
	Calculator()
	{}
	int Calc(string expression)
	{
		Context* pContext = new Context;
		vector<IExpression*> tree;  //语法解析树
		for (int ix = 0; ix < expression.size(); ++ix)
		{
			if (expression[ix] == '+' || expression[ix] == '-')
			{
				tree.push_back(new Operator(expression[ix]));
				cout << "第" << ix << "次压入的符号是" << expression[ix] << endl;
			}
			else
			{
				tree.push_back(new Operand(static_cast<int>(expression[ix] - 48)));
				cout << "第" << ix << "次压入的数字是" << static_cast<int>(expression[ix] - 48) << endl;
			}
		}
		for (vector<IExpression*>::iterator iter = tree.begin(); iter != tree.end(); ++iter)
		{
			(*iter)->Eval(pContext);
		}
		return pContext->GetValue();
	}
};

int main()
{
	Calculator* pc = new Calculator();
	cout << "1+4-2+6+9-5-2-1+1+2= " << pc->Calc("1+4-2+6-9-5-2-1+1+2") << endl;
	system("pause");
	return 0;
}

运行结果

参考资料:
1 https://blog.csdn.net/xiqingnian/article/details/42222369 《大话设计模式C++实现-第27章-解释器模式》
2 https://www.cnblogs.com/hjj-fighting/p/10514935.html 《c++ string 转double》

posted @ 2019-07-11 10:59  尚修能的技术博客  阅读(159)  评论(0编辑  收藏  举报