IT评测·应用市场-qidao123.com

标题: C++ 计划模式——解释器模式 [打印本页]

作者: 用户云卷云舒    时间: 2024-9-9 18:41
标题: C++ 计划模式——解释器模式
C++ 计划模式——解释器模式

解释器模式是一种特定于语言的模式,用于定义怎样评估语言的句法或表达式。它实用于某些重复出现的问题,可以将一个需要解释实行的语言中的句子表示为一个抽象的语法树。这种模式通常被用于开辟编程语言解释器或简单的脚本引擎。
引人“解释器”计划模式的定义(实现意图):定义一个语言的文法(语法规则),并建立一个解释器解释该语言中的句子。
1. 主要组成因素

2. 逐步构建解释器模式

这个逐步构建的过程展示相识释器模式的核心组件怎样协同工作,从定义基本的表达式接口,到实现具体的表达式类,再到构建和解释复杂的表达式树。这种方法使得添加新的表达式类型变得简单,同时保持了团体布局的灵活性和可扩展性。
步调1: 定义抽象表达式

首先定义一个抽象基类 Expression,它是所有表达式的根本,所有的具体表达式类都必须实现这个函数,以便实行具体的解释任务。
  1. //小表达式(节点)父类
  2. class Expression
  3. {
  4. public:
  5.     Expression(int num, char sign) :m_dbg_num(num), m_dbg_sign(sign) {} //构造函数
  6.     virtual ~Expression() {} //做父类时析构函数应该为虚函数
  7. public:
  8.     //解析语法树中的当前节点
  9.     virtual int interpret(map<char, int> var) = 0; //#include <map>,map容器中的键值对用于保存变量名及对应的值
  10. public:
  11.     //以下两个成员变量是为程序跟踪调试时观察某些数据方便而引入
  12.     int m_dbg_num;   //创建该对象时的一个编号,用于记录本对象是第几个创建的
  13.     char m_dbg_sign; //标记本对象的类型,可能是个字符v代表变量(终结符表达式),也可能是个加减号(非终结符表达式)
  14. };
复制代码
步调2: 实现终结符表达式

接着,创建一个或多个终结符表达式类,例如 VarExpression,它们直接与语言的终结符相对应。这些类实现了抽象表达式中定义的 interpret() 方法,返回变量在上下文中的值。
  1. //变量表达式(终结符表达式)
  2. class VarExpression :public Expression
  3. {
  4. public:
  5.     VarExpression(const char& key, int num, char sign) :Expression(num, sign) //构造函数
  6.     {
  7.         m_key = key;
  8.     }
  9.     virtual int interpret(map<char, int> var)
  10.     {
  11.         return var[m_key];  //返回变量名对应的数值
  12.     }
  13. private:
  14.     char m_key; //变量名,本范例中诸如a、b、c、d都是变量名
  15. };
复制代码
步调3: 实现非终结符表达式

创建运算符表达式基类 SymbolExpression 和非终结符表达式类如 AddExpression 和 SubExpression 代表语言的规则。这些类通常会持有其他 Expression 对象,并在其 interpret() 方法中递归调用这些对象的 interpret() 方法,归并其结果。
  1. //运算符表达式(非终结符表达式)父类
  2. class SymbolExpression :public Expression
  3. {
  4. public:
  5.     SymbolExpression(Expression* left, Expression* right, int num, char sign) :m_left(left), m_right(right), Expression(num, sign) {} //构造函数
  6.     Expression* getLeft() { return m_left; }
  7.     Expression* getRight() { return m_right; }
  8. protected:
  9.     //左右各有一个操作数
  10.     Expression* m_left;
  11.     Expression* m_right;
  12. };
  13. //加法运算符表达式(非终结符表达式)
  14. class AddExpression :public SymbolExpression
  15. {
  16. public:
  17.     AddExpression(Expression* left, Expression* right, int num, char sign) :SymbolExpression(left, right, num, sign) {}//构造函数
  18.     virtual int interpret(map<char, int> var)
  19.     {
  20.         //分步骤拆开写,方便理解和观察
  21.         int value1 = m_left->interpret(var); //递归调用左操作数的interpret方法
  22.         int value2 = m_right->interpret(var); //递归调用右操作数的interpret方法
  23.         int result = value1 + value2;
  24.         return result; //返回两个变量相加的结果
  25.     }
  26. };
  27. //减法运算符表达式(非终结符表达式)
  28. class SubExpression :public SymbolExpression
  29. {
  30. public:
  31.     SubExpression(Expression* left, Expression* right, int num, char sign) :SymbolExpression(left, right, num, sign) {}//构造函数
  32.     virtual int interpret(map<char, int> var)
  33.     {
  34.         int value1 = m_left->interpret(var);
  35.         int value2 = m_right->interpret(var);
  36.         int result = value1 - value2;
  37.         return result; //返回两个变量相减的结果
  38.     }
  39. };
复制代码
步调4: 构建语法树

创建一个函数来分析表达式字符串并构建语法树:
  1. //分析—创建语法树(表达式树)
  2. Expression* analyse(string strExp) //strExp:要计算结果的表达式字符串,比如"a-b+c+d"
  3. {
  4.     stack<Expression*>  expStack;//#include <stack>,这里用到了栈这种顺序容器
  5.     Expression* left = nullptr;
  6.     Expression* right = nullptr;
  7.     int icount = 1;
  8.     for (size_t i = 0; i < strExp.size(); ++i)//循环遍历表达式字符串中的每个字符
  9.     {
  10.         switch (strExp[i])
  11.         {
  12.         case '+':
  13.             //加法运算符表达式(非终结符表达式)
  14.             left = expStack.top(); //返回栈顶元素(左操作数)
  15.             ++i;
  16.             right = new VarExpression(strExp[i], icount++, 'v'); //v代表是个变量节点
  17.             //在栈顶增加元素
  18.             expStack.push(new AddExpression(left, right, icount++, '+')); //'+'代表是个减法运算符节点
  19.             break;
  20.         case '-':
  21.             //减法运算符表达式(非终结符表达式)
  22.             left = expStack.top(); //返回栈顶元素
  23.             ++i;
  24.             right = new VarExpression(strExp[i], icount++, 'v');
  25.             expStack.push(new SubExpression(left, right, icount++, '-')); //'-'代表是个减法运算符节点
  26.             break;
  27.         default:
  28.             //变量表达式(终结符表达式)
  29.             expStack.push(new VarExpression(strExp[i], icount++, 'v'));
  30.             break;
  31.         } //end switch
  32.     } //end for
  33.     Expression* expression = expStack.top(); //返回栈顶元素
  34.     return expression;
  35. }
复制代码
步调5: 实现内存管理

添加一个函数来开释表达式树的内存:
  1. void release(Expression* expression)
  2. {
  3.     //释放表达式树的节点内存
  4.     SymbolExpression* pSE = dynamic_cast<SymbolExpression*>(expression); //此处代码有优化空间(不使用dynamic_cast),留给读者思考
  5.     if (pSE)
  6.     {
  7.         release(pSE->getLeft());
  8.         release(pSE->getRight());
  9.     }
  10.     delete expression;
  11. }
复制代码
步调6: 创建上下文和客户端

在 main 函数中创建上下文(变量映射)并使用解释器:
  1. int main()
  2. {
  3.     string strExp = "a-b+c+d";         //将要求值的字符串表达式
  4.     map<char, int> varmap;
  5.     //下面是给字符串表达式中所有参与运算的变量一个对应的数值
  6.     varmap.insert(make_pair('a', 7)); //类似于赋值语句a = 7
  7.     varmap.insert(make_pair('b', 9)); //类似于赋值语句b = 9
  8.     varmap.insert(make_pair('c', 3)); //类似于赋值语句c = 3
  9.     varmap.insert(make_pair('d', 2)); //类似于赋值语句d = 2
  10.     Expression* expression = analyse(strExp);  //调用analyse函数创建语法树
  11.     int result = expression->interpret(varmap); //调用interpret接口求解字符串表达式的结果
  12.     cout << "字符串表达式"a - b + c + d"的计算结果为:" << result << endl; //输出字符串表达式结果
  13.     //释放内存
  14.     release(expression);
  15.     return 0;
  16. }
复制代码
3. 解释器模式 UML 图


UML 图剖析

解释器模式的 UML 图中包含如下 4 种脚色:
4. 解释器模式的长处

5. 解释器模式的缺点

6. 解释器模式实用场景

总结

解释器模式提供了一种灵活的方式来解释特定语言的句子。它将每个文法规则封装到单独的类中,使得语言的解释变得模块化和可扩展。然而,这种模式在处置惩罚复杂语言时可能会导致类的数目激增,而且可能存在性能问题。因此,解释器模式最得当用于简单语言的解释,或者在需要频繁修改语法规则的场景中。在实际应用中,需要权衡其长处和缺点,并根据具体需求决定是否使用此模式。
完整代码

  1. #include <iostream>#include <cstring>#include <map>#include <stack>#include <vector>using namespace std;//小表达式(节点)父类
  2. class Expression
  3. {
  4. public:
  5.     Expression(int num, char sign) :m_dbg_num(num), m_dbg_sign(sign) {} //构造函数
  6.     virtual ~Expression() {} //做父类时析构函数应该为虚函数
  7. public:
  8.     //解析语法树中的当前节点
  9.     virtual int interpret(map<char, int> var) = 0; //#include <map>,map容器中的键值对用于保存变量名及对应的值
  10. public:
  11.     //以下两个成员变量是为程序跟踪调试时观察某些数据方便而引入
  12.     int m_dbg_num;   //创建该对象时的一个编号,用于记录本对象是第几个创建的
  13.     char m_dbg_sign; //标记本对象的类型,可能是个字符v代表变量(终结符表达式),也可能是个加减号(非终结符表达式)
  14. };
  15. //-----//变量表达式(终结符表达式)
  16. class VarExpression :public Expression
  17. {
  18. public:
  19.     VarExpression(const char& key, int num, char sign) :Expression(num, sign) //构造函数
  20.     {
  21.         m_key = key;
  22.     }
  23.     virtual int interpret(map<char, int> var)
  24.     {
  25.         return var[m_key];  //返回变量名对应的数值
  26.     }
  27. private:
  28.     char m_key; //变量名,本范例中诸如a、b、c、d都是变量名
  29. };
  30. //------//运算符表达式(非终结符表达式)父类
  31. class SymbolExpression :public Expression
  32. {
  33. public:
  34.     SymbolExpression(Expression* left, Expression* right, int num, char sign) :m_left(left), m_right(right), Expression(num, sign) {} //构造函数
  35.     Expression* getLeft() { return m_left; }
  36.     Expression* getRight() { return m_right; }
  37. protected:
  38.     //左右各有一个操作数
  39.     Expression* m_left;
  40.     Expression* m_right;
  41. };
  42. //加法运算符表达式(非终结符表达式)
  43. class AddExpression :public SymbolExpression
  44. {
  45. public:
  46.     AddExpression(Expression* left, Expression* right, int num, char sign) :SymbolExpression(left, right, num, sign) {}//构造函数
  47.     virtual int interpret(map<char, int> var)
  48.     {
  49.         //分步骤拆开写,方便理解和观察
  50.         int value1 = m_left->interpret(var); //递归调用左操作数的interpret方法
  51.         int value2 = m_right->interpret(var); //递归调用右操作数的interpret方法
  52.         int result = value1 + value2;
  53.         return result; //返回两个变量相加的结果
  54.     }
  55. };
  56. //减法运算符表达式(非终结符表达式)
  57. class SubExpression :public SymbolExpression
  58. {
  59. public:
  60.     SubExpression(Expression* left, Expression* right, int num, char sign) :SymbolExpression(left, right, num, sign) {}//构造函数
  61.     virtual int interpret(map<char, int> var)
  62.     {
  63.         int value1 = m_left->interpret(var);
  64.         int value2 = m_right->interpret(var);
  65.         int result = value1 - value2;
  66.         return result; //返回两个变量相减的结果
  67.     }
  68. };
  69. //分析—创建语法树(表达式树)
  70. Expression* analyse(string strExp) //strExp:要计算结果的表达式字符串,比如"a-b+c+d"
  71. {
  72.     stack<Expression*>  expStack;//#include <stack>,这里用到了栈这种顺序容器
  73.     Expression* left = nullptr;
  74.     Expression* right = nullptr;
  75.     int icount = 1;
  76.     for (size_t i = 0; i < strExp.size(); ++i)//循环遍历表达式字符串中的每个字符
  77.     {
  78.         switch (strExp[i])
  79.         {
  80.         case '+':
  81.             //加法运算符表达式(非终结符表达式)
  82.             left = expStack.top(); //返回栈顶元素(左操作数)
  83.             ++i;
  84.             right = new VarExpression(strExp[i], icount++, 'v'); //v代表是个变量节点
  85.             //在栈顶增加元素
  86.             expStack.push(new AddExpression(left, right, icount++, '+')); //'+'代表是个减法运算符节点
  87.             break;
  88.         case '-':
  89.             //减法运算符表达式(非终结符表达式)
  90.             left = expStack.top(); //返回栈顶元素
  91.             ++i;
  92.             right = new VarExpression(strExp[i], icount++, 'v');
  93.             expStack.push(new SubExpression(left, right, icount++, '-')); //'-'代表是个减法运算符节点
  94.             break;
  95.         default:
  96.             //变量表达式(终结符表达式)
  97.             expStack.push(new VarExpression(strExp[i], icount++, 'v'));
  98.             break;
  99.         } //end switch
  100.     } //end for
  101.     Expression* expression = expStack.top(); //返回栈顶元素
  102.     return expression;
  103. }
  104. void release(Expression* expression)
  105. {
  106.     //释放表达式树的节点内存
  107.     SymbolExpression* pSE = dynamic_cast<SymbolExpression*>(expression); //此处代码有优化空间(不使用dynamic_cast),留给读者思考
  108.     if (pSE)
  109.     {
  110.         release(pSE->getLeft());
  111.         release(pSE->getRight());
  112.     }
  113.     delete expression;
  114. }
  115. int main()
  116. {
  117.     string strExp = "a-b+c+d";         //将要求值的字符串表达式
  118.     map<char, int> varmap;
  119.     //下面是给字符串表达式中所有参与运算的变量一个对应的数值
  120.     varmap.insert(make_pair('a', 7)); //类似于赋值语句a = 7
  121.     varmap.insert(make_pair('b', 9)); //类似于赋值语句b = 9
  122.     varmap.insert(make_pair('c', 3)); //类似于赋值语句c = 3
  123.     varmap.insert(make_pair('d', 2)); //类似于赋值语句d = 2
  124.     Expression* expression = analyse(strExp);  //调用analyse函数创建语法树
  125.     int result = expression->interpret(varmap); //调用interpret接口求解字符串表达式的结果
  126.     cout << "字符串表达式"a - b + c + d"的计算结果为:" << result << endl; //输出字符串表达式结果
  127.     //释放内存
  128.     release(expression);
  129.     return 0;
  130. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4