解析器模式c++实现于理解
标签: 设计模式
下面实现一个将字符串表达式进行解析计算的例子。
首先定义表达式的抽象类
//抽象表达式类
class Expression
{
public:
virtual int interpreter(unordered_map<string, int> var) = 0;
};
因为表达式分为符号表达式和变量表达式,变量表达式就是代表一个数值而已,符号表达式则意味着计算。
下面定义变量解析器
//变量解析器
class VarExpression :public Expression
{
private:
string _Key;
public:
VarExpression(string Key):_Key(Key)
{
}
int interpreter(unordered_map<string, int> var)
{
return var[_Key];
}
};
unordered_map<string, int> var是储存变量名称及其对应数值的容器。可见变量表达式的功能很简单,就是返回变量名对应的数值。
下面给出符号表达式的抽象类定义
//抽象符号解析器
class SymbolExpression :public Expression
{
protected:
Expression* _left;
Expression* _right;
public:
SymbolExpression(Expression* left, Expression* right) :_left(left), _right(right) {};
};
符号表达式需要左边、右边两个计算元素才能运作。故需要两个表达式left和right初始化。
下面给出两种具体的符号表达式实现:
//加法解析器
class AddExpression :public SymbolExpression
{
public:
AddExpression(Expression* left, Expression* right) :SymbolExpression::SymbolExpression(left, right) {};
int interpreter(unordered_map<string, int> var)
{
return _left->interpreter(var) + _right->interpreter(var);
}
};
//减法解析器
class SubExpression :public SymbolExpression
{
public:
SubExpression(Expression* left, Expression* right) :SymbolExpression::SymbolExpression(left, right) {};
int interpreter(unordered_map<string, int> var)
{
return _left->interpreter(var) - _right->interpreter(var);
}
};
这里采用了递归计算的方式,首先计计算出左右表达式的返回值再取其和或者差返回。
为了更好的掌控这些解析器,定义计算器类如下:
//四则计算器
class Calculation
{
private:
string _expstr;
unordered_map<string, int> _var;
vector<string> _var_arr;
public:
Expression* expression;
Calculation(string expstr)
{
_expstr = expstr;
}
void SetExpression(string expstr)
{
_expstr = expstr;
}
//检查参数的值是否赋值
void SetValue()
{
unsigned int left = 0;
int len = _expstr.length();
for (int i = 0;i < len;i++)
{
if (_expstr[i] == '+' || _expstr[i] == '-')
{
if (i == 0 || i == len - 1)
{
cout << "表达式错误" << endl;
return;
}
else
{
string temp = _expstr.substr(left, i - left);
if (_var.count(temp) <= 0)
{
cout << "请为变量" << temp << "赋值:";
int val;
cin >> val;
_var[temp] = val;
}
left = i+1;
_var_arr.push_back(temp);
}
}
if (i == len-1)
{
string temp = _expstr.substr(left, len - left);
if (_var.count(temp) <= 0)
{
cout << "请为变量" << temp << "赋值:";
int val;
cin >> val;
_var[temp] = val;
}
left = i+1;
_var_arr.push_back(temp);
}
}
}
void cal()
{
SetValue();
stack<Expression*> stack;
int var_ind = 0;
for (auto x : _expstr)
{
switch (x)
{
case '+':
{
if (stack.empty())
{
stack.push(new VarExpression(_var_arr[var_ind++]));
};
auto left = stack.top();
stack.pop();
VarExpression* right = new VarExpression(_var_arr[var_ind++]);
stack.push(new AddExpression(left, right));
break;
}
case '-':
{
if (stack.empty())
{
stack.push(new VarExpression(_var_arr[var_ind++]));
};
auto left = stack.top();
stack.pop();
VarExpression* right = new VarExpression(_var_arr[var_ind++]);
stack.push(new SubExpression(left, right));
break;
}
default:
break;
}
}
cout << "计算结果为: " << stack.top()->interpreter(_var) << endl;
}
};
这里有单个主要的类成员:
string _expstr:j需要计算的目标
unordered_map<string, int> _var:变量与数值的对应关系
vector _var_arr:变量在表达式中出现的顺序
主要有两个函数,一个是初始化函数SetValue,用于对表达式中的所有变量进行赋值,初始化_var和_var_arr;
cal()用于计算结果。该函数主要通过不断将两个变量表达式和一个符号结合形成一个新的符号表达式的方式,最终将整个目标字符串变为一个符号表达式的方式来计算答案。
下面是调用实例
int main()
{
Calculation cal("zhangsan+lisi+wangwu-didi+zhangsan+lisi-didi");
cal.cal();
return 0;
}
下面谈谈我对解析器模式的理解。解析器模式实现的功能其实通过递归算法或者迭代都可以计算出来,还记得在leetcode上刷到过类似于这种字符串表达式判断是否正确的问题。
那么迭代器模式的好处在哪里呢?还是在于解耦,通过迭代器模式,可以新增各种符号变量进去,比如现在可以很方便的*与/添加进去,唯一需要修改的类就是计算器类。改动相对较少。
其次用单独某个算法实现的话,会造成某类功能太多,过于臃肿,承担了超过一个责任。