C++设计模式:解释器模式(简单的数学表达式解析器)

什么是解释器模式?

解释器模式是一种行为型设计模式,用于为特定的语言定义一个解释器,解释并执行语言中的句子。它主要用于构建一个简单的语法解释器,将特定的业务逻辑转化为可理解的语言表达,并对这些表达式进行求值或解析。


通俗解释

想象一个简单的场景:
你想实现一个计算器,用来解析并计算数学表达式,例如:3 + 5 - 2

  • 表达式中的每个元素(如35+-)是语言的基本符号。
  • 解释器模式会为这些符号定义一个语法规则,然后按照规则逐步解析表达式,并计算结果。

解释器模式就是将这些规则封装到类中,让这些类像搭积木一样组成解析器,最终解释并执行语句。


解释器模式的特点

  1. 递归结构

    • 解释器模式通常以递归的方式解析复杂的表达式,将表达式分解为更小的子表达式。
  2. 语法树

    • 表达式通常可以通过抽象语法树表示,每个节点都是一个表达式或操作符。
  3. 适用领域

    • 适合为特定领域定义简单的语言或语法(如正则表达式、数学解析器等)。

解释器模式的结构

UML 类图
       +------------------------+|    AbstractExpression |  // 抽象表达式+------------------------+|  + interpret(context)  |+------------------------+^           ^|           |+----------------+  +------------------+| TerminalExpr   |  | NonTerminalExpr  |  // 终结符表达式和非终结符表达式+----------------+  +------------------+| + interpret()  |  | + interpret()    |+----------------+  +------------------+

组成部分
  1. AbstractExpression(抽象表达式)

    • 定义了解释器的通用接口,所有表达式都必须实现这个接口。
  2. TerminalExpression(终结符表达式)

    • 代表语法中的基本符号(如数字、变量)。
    • 无需进一步解析。
  3. NonTerminalExpression(非终结符表达式)

    • 代表语法中的操作符(如加法、减法)。
    • 由其他表达式组成,通常包含递归结构。
  4. Context(上下文)

    • 提供解释器所需的全局信息,例如变量的值或其他共享数据。

解释器模式的优缺点

优点
  1. 扩展性强

    • 新的语法规则可以通过添加新的表达式类轻松扩展。
  2. 逻辑清晰

    • 语法规则以类的形式清晰表达,便于维护。
  3. 适合特定领域

    • 非常适合为某些特定领域的语言定义解释器。
缺点
  1. 性能问题

    • 复杂语法可能需要大量的类来表示,增加了系统的复杂性。
  2. 维护困难

    • 如果语法规则复杂,会导致类和对象关系过于繁琐,维护成本较高。

案例:简单的数学表达式解析器

需求描述

实现一个简单的数学解析器,用于计算由加法和减法组成的表达式,例如:
3 + 5 - 2

解析器需要支持以下功能:

  1. 支持整数的加法和减法。
  2. 将表达式拆分为子表达式,并按照语法规则解析。

完整代码实现

以下是解释器模式在数学解析器中的实现,输出为中文,附详细注释。

#include <iostream>
#include <memory>
#include <string>
#include <sstream>
#include <vector>// 抽象表达式类
class Expression {
public:virtual int interpret() const = 0; // 解析并返回结果virtual ~Expression() = default;
};// 终结符表达式类:数字
class NumberExpression : public Expression {
private:int number; // 存储的数字public:explicit NumberExpression(int value) : number(value) {}int interpret() const override {return number; // 返回数字值}
};// 非终结符表达式类:加法
class AddExpression : public Expression {
private:std::shared_ptr<Expression> left;  // 左操作数std::shared_ptr<Expression> right; // 右操作数public:AddExpression(std::shared_ptr<Expression> left, std::shared_ptr<Expression> right): left(left), right(right) {}int interpret() const override {return left->interpret() + right->interpret(); // 返回加法结果}
};// 非终结符表达式类:减法
class SubtractExpression : public Expression {
private:std::shared_ptr<Expression> left;  // 左操作数std::shared_ptr<Expression> right; // 右操作数public:SubtractExpression(std::shared_ptr<Expression> left, std::shared_ptr<Expression> right): left(left), right(right) {}int interpret() const override {return left->interpret() - right->interpret(); // 返回减法结果}
};// 客户端代码:解析输入表达式
std::shared_ptr<Expression> parseExpression(const std::string& input) {std::istringstream iss(input);std::vector<std::shared_ptr<Expression>> expressions;char op;int value;// 读取第一个数字iss >> value;expressions.push_back(std::make_shared<NumberExpression>(value));// 解析后续操作符和数字while (iss >> op >> value) {std::shared_ptr<Expression> right = std::make_shared<NumberExpression>(value);if (op == '+') {expressions.push_back(std::make_shared<AddExpression>(expressions.back(), right));} else if (op == '-') {expressions.push_back(std::make_shared<SubtractExpression>(expressions.back(), right));}// 弹出旧的表达式,保留最新结果表达式expressions.erase(expressions.begin(), expressions.end() - 1);}// 返回最后一个表达式return expressions.back();
}// 主函数
int main() {std::string input;std::cout << "请输入数学表达式(例如:3 + 5 - 2): ";std::getline(std::cin, input);// 解析表达式并计算结果auto expression = parseExpression(input);int result = expression->interpret();std::cout << "表达式的计算结果为: " << result << std::endl;return 0;
}

运行结果

假设输入以下内容:

3 + 5 - 2

输出结果为:

表达式的计算结果为: 6

代码解析

1. 抽象表达式类(Expression
class Expression {
public:virtual int interpret() const = 0;virtual ~Expression() = default;
};
  • 定义了解释器的通用接口。
  • interpret()方法返回当前表达式的值。

2. 终结符表达式类(NumberExpression
class NumberExpression : public Expression {
private:int number;public:explicit NumberExpression(int value) : number(value) {}int interpret() const override {return number;}
};
  • 表示数字。
  • interpret()方法直接返回数字值。

3. 非终结符表达式类(AddExpressionSubtractExpression
  • 加法表达式
int interpret() const override {return left->interpret() + right->interpret();
}
  • 减法表达式
int interpret() const override {return left->interpret() - right->interpret();
}
  • 这两类表达式将左右子表达式的值进行运算,并返回结果。

4. 解析器函数(parseExpression
std::shared_ptr<Expression> parseExpression(const std::string& input) {// 解析表达式并构建语法树...
}
  • 将输入的字符串解析为抽象语法树。
  • 每次读取一个操作符和一个数字,根据操作符创建对应的表达式对象。

解释器模式的应用场景

  1. 数学表达式解析
    • 如计算器、公式解析器。
  2. 脚本语言解释器
    • 如自定义的配置文件解析。
  3. 正则表达式解析
    • 如正则表达式的引擎。

总结

解释器模式的核心优势
  1. 可扩展性强:添加新的语法规则只需新增类,无需修改现有代码。
  2. 逻辑清晰:将语法规则封装为类,便于维护和复用。
注意事项
  1. 性能开销

:对于复杂的语法规则,可能会导致大量的类和对象,增加系统复杂性。
2. 适用领域有限:适合简单的语法,复杂语言的解释器更适合通过生成器工具实现(如ANTLR)。

通过本案例,我们展示了如何使用解释器模式构建一个简单的数学解析器,轻松实现对表达式的解析和计算。解释器模式非常适合用在自定义语言、规则引擎和公式计算等场景中!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/890742.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【深度学习环境】NVIDIA Driver、Cuda和Pytorch(centos9机器,要用到显示器)

文章目录 一 、Anaconda install二、 NIVIDIA driver install三、 Cuda install四、Pytorch install 一 、Anaconda install Step 1 Go to the official website: https://www.anaconda.com/download Input your email and submit. Step 2 Select your version, and click i…

寻找适合小户型的开源知识库open source knowledge base之路

寻找一个开源的知识库&#xff0c;为了把以前花很多时间收集的信息或是项目/课程资料放到一个容易归类和管理的私有自主系统中&#xff0c;以便更容易查阅&#xff0c;花更少时间收集、对比版本及分享等一系列管理工作&#xff0c;同时确保在需要时可以相对快速找到有用的资料&…

C语言勘破之路-最终篇 —— 预处理(上)

人无完人&#xff0c;持之以恒&#xff0c;方能见真我&#xff01;&#xff01;&#xff01; 共同进步&#xff01;&#xff01; 文章目录 一、预定义符号二、#define定义常量三.、#define定义宏四、带有副作用的宏参数五、宏替换的规则六、宏和函数的对比1.宏的优势2.函数的优…

学习threejs,THREE.RingGeometry 二维平面圆环几何体

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.RingGeometry 圆环几…

Win11系统下Oracle11g数据库下载与安装使用教程

文章目录 一、Oracle下载与安装1.1 解压安装包1.2 开始安装Oracle11g1.2.1 用户 1.3 测试数据库是否配置成功1.4 了解一下 Oracle相关服务1.5 了解Oracle体系结构 二、使用工具连接数据库2.1 PL/ SQL 连接本地oracle 三、PL/ SQL远程访问数据库3.1 可能踩坑问题&#xff08;TNS…

数据结构(Java版)第六期:LinkedList与链表(一)

目录 一、链表 1.1. 链表的概念及结构 1.2. 链表的实现 专栏&#xff1a;数据结构(Java版) 个人主页&#xff1a;手握风云 一、链表 1.1. 链表的概念及结构 链表是⼀种物理存储结构上⾮连续存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的引⽤链接次序实现的。与火车…

从零开始C++棋牌游戏开发之第三篇:游戏的界面布局设计

在游戏开发的旅途中&#xff0c;界面布局设计是一个充满创意和挑战的环节。对于棋牌类游戏而言&#xff0c;界面不仅仅是功能的载体&#xff0c;更是玩家与游戏互动的桥梁。一个清晰、直观且美观的界面可以显著提升游戏的用户体验。 在这篇文章中&#xff0c;我们将从功能需求…

计算机基础知识——数据结构与算法(五)(山东省大数据职称考试)

大数据分析应用-初级 第一部分 基础知识 一、大数据法律法规、政策文件、相关标准 二、计算机基础知识 三、信息化基础知识 四、密码学 五、大数据安全 六、数据库系统 七、数据仓库. 第二部分 专业知识 一、大数据技术与应用 二、大数据分析模型 三、数据科学 数据结构与算法…

使用 Python 为 PDF 添加水印

概述 安装所需库创建水印 PDF将水印应用到你的 PDF 1. 安装所需库 首先&#xff0c;确保你的系统上安装了 Python。然后&#xff0c;使用 pip 安装必要的库&#xff1a; pip install PyPDF2 reportlabPyPDF2&#xff1a;一个用于读取和操作 PDF 文件的库。reportlab&#x…

数据库管理-第275期 Oracle 23ai:画了两张架构图(20241225)

数据库管理275期 2024-12-25 数据库管理-第275期 Oracle 23ai&#xff1a;画了两张架构图&#xff08;20241225&#xff09;1 系统管理分片2 用户定义分片总结 数据库管理-第275期 Oracle 23ai&#xff1a;画了两张架构图&#xff08;20241225&#xff09; 作者&#xff1a;胖…

C++ 面向对象编程

面向对象编程&#xff08;Object-Oriented Programming, OOP&#xff09;是C语言的一个重要特性&#xff0c;它允许开发者以更直观和模块化的方式来设计和构建程序。OOP的四个主要原则是&#xff1a;封装&#xff08;Encapsulation&#xff09;、继承&#xff08;Inheritance&a…

增强路由器 路由器升级宽带速度

由器中DNS设置 DNS&#xff08;域名系统&#xff09;是什么&#xff1f; DNS将域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使人更方便地访问互联网。DNS使用UDP端口53。 上网前提是&#xff1a;配置 IPv4地址、子网掩码 、网关、DNS 正确才能够上网 DNS填写规…

stm32制作CAN适配器5--WinUsb上位机编写

上次我们要stm32制作了一个基于winusb有canfd适配器&#xff0c;今天我们来制作一个上位机程序来进行报文收发。 上位机还是用以前写好的&#xff0c;只是更改下dll文件。 项目链接器&#xff0c;输入&#xff0c;附加依赖项中增加winusb.lib winusb初始化&#xff1a;#incl…

谷歌浏览器 Chrome 提示:此扩展程序可能很快将不再受支持

问题现象 在Chrome 高版本上的扩展管理页面&#xff08;地址栏输入chrome://extensions/或者从界面进入&#xff09;&#xff1a; &#xff0c; 可以查看到扩展的情况。 问题现象大致如图: 问题原因 出现此问题的根本原因在于&#xff1a;谷歌浏览器本身的扩展机制发生了…

Vue.js组件(6):echarts组件

1 前言 本章主要对常用的echars图表展示进行基本的组件封装。使用该组件前需要在项目中引入echarts。官网&#xff1a;Apache ECharts npm install echarts --save 2 图表组件 2.1 折线图组件 组件属性&#xff1a;chartId&#xff0c;指定图表挂载div的id&#xff0c;注意不…

C#—LINQ详解及汇总

LINQ详解及汇总 LINQ&#xff08;Language Integrated Query&#xff09;是微软的一项技术&#xff0c;允许开发者以一种简洁的方式查询和操作数据&#xff0c;支持多种数据源&#xff0c;包括对象、数据库、XML和数据集。LINQ定义了约40个查询操作符&#xff0c;如select、fr…

【Python高级353】python实现多线程版本的TCP服务器

前面学了了套接字编程、tcp服务端客户端开发、面向对象版的服务端客户端、带有端口复用的服务端。 这里使用多线程开发多任务版的服务端 多任务版本的TCP服务器 来一个客户&#xff0c;就为其创建一个线程 import socket import threadingclass WebServer:# 3、定义一个__ini…

MySQL用表组织数据

用表组织数据 文章目录 用表组织数据一.四种完整性约束二.数值类型2-1三.数值类型2-2四.字符串.日期类型五.设置1.设置主键2.设置标识列3.设置非空4.设置默认值 六.主外键建立后注意事项 一.四种完整性约束 1.域完整性 列 域完整性约束方法:限制数据类型,检查约束,外键约束,默…

面试经典问题 —— 最大/小前K个数问题(top - K)问题

目录 常见思路更优的解法&#xff08;面试官喜欢的&#xff09; 常见思路 要选出最小的前K个数首先我们会想到排排升序建大堆&#xff0c;排降序建小堆 一个直观的想法是使用&#xff08;小根堆&#xff09;&#xff0c;起始将所有元素放入堆中&#xff0c;然后再从堆中取出k 个…

外包干了27天,技术退步明显。。。。。

时光荏苒&#xff0c;转眼我已是一个拥有近四年功能测试经验的大专生。20年&#xff0c;我满怀激情地通过校招进入湖南某知名软件公司&#xff0c;期待在这里开启我的职业生涯。然而&#xff0c;长时间的舒适环境让我渐渐失去了前进的动力&#xff0c;技术停滞不前&#xff0c;…