突破编程_C++_设计模式(解释器模式)

1 解释器模式的基本概念

C++ 解释器模式的基本概念主要涉及到给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

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

解释器模式类结构中包含了多个角色,例如:

(1)AbstractExpression: 抽象表达式,声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。

(2)Context: 解释器上下文环境类,用来存储解释器上下文环境,比如需要解释的文法等。
解释器模式的应用场景主要是文法简单的情况。对于复杂的文法,文法的类层次会变得庞大而无法管理,此时语法分析程序生成器这样的工具是更好的选择。同时,解释器模式并不总是追求效率,最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式。

总体而言,C++ 解释器模式是一种用于处理和解释特定语言句子的设计模式,它可以帮助我们更好地理解和解决特定类型的问题。

2 解释器模式的实现步骤

解释器模式的实现步骤如下:

(1)定义抽象表达式(AbstractExpression):

  • 创建一个抽象基类,用于表示文法中的表达式。
  • 定义解释方法(interpret),该方法负责执行具体的解释逻辑。
  • 根据需要,可以定义其他方法或属性,以支持解释过程。

(2)实现具体表达式(TerminalExpression 和 NonterminalExpression):

  • 根据文法的具体规则,创建继承自抽象表达式的具体表达式类。
  • 对于文法中的终结符(Terminal),创建TerminalExpression类,并实现解释方法。
  • 对于文法中的非终结符(Nonterminal),创建NonterminalExpression类,并实现解释方法。非终结符通常包含对其他表达式的引用,并在解释方法中调用它们的解释方法。

(3)定义上下文环境(Context):

  • 创建一个上下文环境类,用于存储解释过程中需要的信息。
  • 上下文环境可以包含全局变量、辅助函数等,供解释器在解释过程中使用。

(4)构建抽象语法树(AST):

  • 根据输入的句子或表达式,构建对应的抽象语法树。
  • 每个节点对应一个具体表达式对象,根据文法的结构,将节点组织成树状结构。

(5)实现解释器(Interpreter):

  • 创建一个解释器类,负责调用抽象语法树的根节点进行解释。
  • 解释器接收上下文环境作为参数,并在解释过程中使用它。
  • 解释器从根节点开始,递归地调用节点的解释方法,直到整个语法树被解释完毕。

(6)使用解释器:

  • 初始化上下文环境,并构建抽象语法树。
  • 创建解释器对象,并传入上下文环境。
  • 调用解释器的解释方法,开始解释过程。
  • 解释器根据语法树的结构和节点的解释方法,逐步执行解释操作,并产生相应的结果。

如下为样例代码:

#include <iostream>  
#include <string>  
#include <vector>  
#include <memory>  // 上下文环境  
class Context {
public:void addOutput(const std::string& value) {output.push_back(value);}std::string input;std::vector<std::string> output;
};// 抽象表达式  
class AbstractExpression {
public:virtual ~AbstractExpression() {}virtual void interpret(Context& context) = 0;
};// 具体表达式 - 终结符  
class TerminalExpression : public AbstractExpression {
public:void interpret(Context& context) override {context.addOutput("Terminal Expression interpreted");}
};// 具体表达式 - 非终结符  
class NonterminalExpression : public AbstractExpression {
public:NonterminalExpression(std::unique_ptr<AbstractExpression> l, std::unique_ptr<AbstractExpression> r): left(std::move(l)), right(std::move(r)) {}void interpret(Context& context) override {context.addOutput("Nonterminal Expression interpreted (");left->interpret(context);context.addOutput(", ");right->interpret(context);context.addOutput(")");}private:std::unique_ptr<AbstractExpression> left;std::unique_ptr<AbstractExpression> right;
};// 解释器  
class Interpreter {
public:Interpreter(std::unique_ptr<AbstractExpression> exp) : expression(std::move(exp)) {}void interpret(Context& context) {expression->interpret(context);}private:std::unique_ptr<AbstractExpression> expression;
};int main() 
{// 创建具体表达式  auto terminal = std::make_unique<TerminalExpression>();auto nonterminal = std::make_unique<NonterminalExpression>(std::make_unique<TerminalExpression>(),std::make_unique<TerminalExpression>());// 创建上下文环境  Context context;context.input = "Sample input";// 创建解释器  Interpreter interpreter(std::move(nonterminal));// 解释执行  interpreter.interpret(context);// 输出结果  for (const auto& output : context.output) {std::cout << output << std::endl;}return 0;
}

上面代码的输出为:

Nonterminal Expression interpreted (
Terminal Expression interpreted
,
Terminal Expression interpreted
)

这个样例代码定义了一个简单的解释器模式。它包含一个抽象表达式类 AbstractExpression,一个上下文环境类 Context,两个具体表达式类 TerminalExpression 和 NonterminalExpression,以及一个解释器类 Interpreter。

在 main 函数中,创建了一个 TerminalExpression 实例和一个包含两个 TerminalExpression 实例的 NonterminalExpression 实例。然后,创建了一个上下文环境实例,并初始化了一些输入数据。接下来,创建了一个解释器实例,并将 NonterminalExpression 实例传递给解释器。最后,调用解释器的 interpret 方法来执行解释操作,并输出结果。

3 解释器模式的应用场景

C++ 解释器模式的应用场景广泛,主要适用于以下情况:

(1)配置文件解析: 应用程序的配置文件通常包含特定的语法规则,用于定义各种设置和参数。解释器模式可以用于解析这些配置文件,并将其转换为程序可以理解的格式。

(2)表达式求值: 在某些应用中,可能需要动态地解析和执行数学表达式、逻辑表达式或其他类型的表达式。解释器模式允许你定义这些表达式的语法,并提供解释器来执行它们。

(3)文本处理: 在处理文本文件或字符串时,可能需要根据特定的语法规则来解析和提取信息。解释器模式可以帮助你定义这些规则,并提供相应的解释器来处理文本数据。

(4)自定义编程语言解释: 当需要创建自己的编程语言时,解释器模式非常有用。则可以定义语言的语法规则,并使用解释器模式来解析和执行这些规则。这样可以实现语言的灵活性和可扩展性。

(5)业务规则引擎: 在某些业务应用中,可能需要根据一系列复杂的业务规则来做出决策。解释器模式允许你定义这些规则,并提供解释器来根据这些规则进行推理和决策。

3.1 解释器模式应用于配置文件解析

如下是一个使用解释器模式进行配置文件解析的示例:

#include <iostream>  
#include <memory>  
#include <string>  
#include <unordered_map>  // 抽象表达式  
class AbstractExpression {
public:virtual ~AbstractExpression() {}virtual void interpret(const std::string& line, std::unordered_map<std::string, std::string>& values) = 0;
};// 具体表达式 - 键值对解析  
class KeyValueExpression : public AbstractExpression {
public:void interpret(const std::string& line, std::unordered_map<std::string, std::string>& values) override {size_t pos = line.find('=');if (pos != std::string::npos) {std::string key = line.substr(0, pos);std::string value = line.substr(pos + 1);values[key] = value;std::cout << "Parsed key-value pair: " << key << " = " << value << std::endl;}else {std::cout << "Invalid line format: " << line << std::endl;}}
};// 解释器上下文  
class Context {
public:std::unordered_map<std::string, std::string> values;
};// 解释器  
class Interpreter {
public:Interpreter(std::unique_ptr<AbstractExpression> exp) : expression(std::move(exp)) {}void interpret(const std::string& line, Context& context) {expression->interpret(line, context.values);}private:std::unique_ptr<AbstractExpression> expression;
};int main() 
{// 创建具体表达式实例  auto keyValueExpr = std::make_unique<KeyValueExpression>();// 创建解释器上下文  Context context;// 创建解释器  Interpreter interpreter(std::move(keyValueExpr));// 示例配置文件内容  std::vector<std::string> configLines = {"username=zhangsan","password=123456@654321","server=example.com","invalid_line" // 无效行,应该被忽略  };// 解析配置文件  for (const auto& line : configLines) {interpreter.interpret(line, context);}// 输出解析结果  for (const auto& pair : context.values) {std::cout << "Parsed value for " << pair.first << ": " << pair.second << std::endl;}return 0;
}

上面代码的输出为:

Parsed key-value pair: username = zhangsan
Parsed key-value pair: password = 123456@654321
Parsed key-value pair: server = example.com
Invalid line format: invalid_line
Parsed value for username: zhangsan
Parsed value for password: 123456@654321
Parsed value for server: example.com

这个示例中定义了一个 AbstractExpression 抽象类,它有一个 interpret 方法需要子类来实现。KeyValueExpression 类继承自 AbstractExpression,并实现了 interpret 方法以解析键值对。

Context 类用于存储解析得到的键值对。

Interpreter 类负责使用具体的表达式实例来解析配置文件中的每一行。它接收一个 AbstractExpression的 智能指针,并在 interpret 方法中调用该表达式的 interpret 方法。

在 main 函数中,创建了一个 KeyValueExpression 的实例,并将其传递给 Interpreter。然后,遍历示例配置文件中的每一行,并使用解释器来解析它们。最后,输出解析得到的键值对。

3.2 解释器模式应用于表达式求值

如下是一个使用解释器模式进行表达式求值的示例

#include <iostream>  
#include <memory>  
#include <stdexcept>  // 抽象表达式  
class AbstractExpression {
public:virtual ~AbstractExpression() {}virtual double interpret() = 0;
};// 数字表达式  
class NumberExpression : public AbstractExpression {
public:NumberExpression(double val) : value(val) {}double interpret() override { return value; }
private:double value;
};// 加法表达式  
class AdditionExpression : public AbstractExpression {
public:AdditionExpression(std::unique_ptr<AbstractExpression> l, std::unique_ptr<AbstractExpression> r): left(std::move(l)), right(std::move(r)) {}double interpret() override {return left->interpret() + right->interpret();}
private:std::unique_ptr<AbstractExpression> left;std::unique_ptr<AbstractExpression> right;
};// 解释器上下文(在这个例子中,上下文没有额外信息,因此可以省略)  // 解释器  
class Interpreter {
public:Interpreter() {}// 解析并计算表达式  double interpret(std::unique_ptr<AbstractExpression> expression) {return expression->interpret();}
};int main() 
{// 创建表达式树  auto five = std::make_unique<NumberExpression>(5.0);auto three = std::make_unique<NumberExpression>(3.0);auto sum = std::make_unique<AdditionExpression>(std::move(five), std::move(three));// 创建解释器  Interpreter interpreter;// 计算表达式的结果  double result = interpreter.interpret(std::move(sum));// 输出结果  std::cout << "The result of the expression is: " << result << std::endl;return 0;
}

上面代码的输出为:

The result of the expression is: 8

这个例子中定义了 AbstractExpression 作为所有表达式的基类,并提供了 interpret 方法来执行实际的求值。NumberExpression 类代表一个数字值,而 AdditionExpression 类代表两个表达式之间的加法运算。

在 main 函数中,创建了一个表达式树,其中 five 和 three 是叶子节点(数字表达式),而 sum 是根节点(加法表达式)。将这些表达式对象封装在 std::unique_ptr 中,并通过 std::move 来传递所有权。

最后,创建一个 Interpreter 对象,并调用其 interpret 方法来计算表达式树的结果。结果输出到控制台。

4 解释器模式的优点与缺点

C++ 解释器模式的优点主要包括:

(1)易于实现文法: 在解释器模式中,每一条语法规则都被封装在一个解释器对象中。这使得每条规则的实现变得相对简单,开发者只需要关注特定规则的解析,而无需考虑其他规则。

(2)易于扩展新的语法: 由于解释器模式采用类来描述语法规则,因此通过继承等机制可以轻松地创建新的解释器对象来扩展新的语法规则。

(3)灵活性: 解释器模式提供了很大的灵活性,允许开发者根据需要对语法规则进行定制和修改。

然而,C++ 解释器模式也存在一些缺点:

(1)执行效率较低: 解释器模式在解析复杂的语法结构时,通常需要进行大量的循环和递归调用,这可能导致解析速度变慢,尤其在处理大型或复杂的输入时。此外,解释器模式的代码调试过程也可能相对复杂。

(1)对于复杂文法难以维护: 如果语言包含大量的语法规则,那么解释器模式中的每个规则都需要定义一个类,这可能导致类的数量急剧增加,使得系统难以管理和维护。在这种情况下,可能需要考虑使用其他方法,如语法分析程序,来替代解释器模式。

(1)资源消耗: 由于每个语法规则都需要一个解释器对象来处理,这可能导致大量的对象创建和销毁,从而增加内存使用和垃圾回收的开销。

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

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

相关文章

【vue baidu-map】实现百度地图展示基地,鼠标悬浮标注点展示详细信息

实现效果如下&#xff1a; 自用代码记录 <template><div class"map" style"position: relative;"><baidu-mapid"bjmap":scroll-wheel-zoom"true":auto-resize"true"ready"handler"><bm-mar…

为何虎茅掌柜 短短6个月会员近百万 月销售额破亿!

大家好 我是吴军 一家软件开发公司的产品经理 今天我们来分析一下&#xff0c;一个卖酒的模式 为什么这家平台短短6个月&#xff0c;发展百万名用户&#xff0c;月销售额破亿! 虎茅掌柜&#xff0c;切记这个虎茅和茅台的那个没有任何关系 虎茅掌柜 虎茅掌柜实质上是一种…

Spring Bean的生命周期流程

前言 Java 中的公共类称之为Java Bean&#xff0c;而 Spring 中的 Bean 指的是将对象的生命周期&#xff0c;交给Spring IoC 容器来管理的对象。所以 Spring 中的 Bean 对象在使用时&#xff0c;无需通过 new 来创建对象&#xff0c;只需要通过 DI&#xff08;依赖注入&#x…

2024 MCM数学建模美赛2024年A题复盘,思路与经验分享:资源可用性与性别比例 | 七鳃鳗的性别比例变化对生态系统稳定性的影响(四)

审题 第三问要我们评估七鳃鳗的性别比例变化对生态系统稳定性的影响。 这里我们就要去查一下生态系统稳定性的定义。 通过查资料我们知道&#xff0c;生态系统稳定性包括生态系统的抵抗力和恢复力。 OK&#xff0c;到这里问题就变成了&#xff0c;七鳃鳗的性别比例对生态系…

漏洞复现-H3C系列

漏洞复现-H3C H3C 用户自助服务平台远程命令执行漏洞H3C堡垒机H3C防火墙 admin/adminH3C root/h3c123.com【漏洞复现】华三用户自助服务产品dynamiccontent.properties.xhtml接口处存在RCE漏洞H3C交换机H3C cas_cvm_upload-RCE (默认写入冰蝎4.0. 3aes)H3C CVM任意文件上传漏洞…

用crypto库的哈希函数CryptoPP::SHA256实现最简单的区块链20240101

最简单的区块链代码: Crypto 库和 OpenSSL 库中的哈希函数比较 Crypto 库和 OpenSSL 库都提供了各种哈希函数&#xff0c;包括 SHA256 函数。 效率方面 两者在哈希计算速度上都比较接近&#xff0c;但 Crypto 库可能略快一些。Crypto 库提供了多线程支持&#xff0c;可以进一…

Java 根据IP获取IP地址信息(离线)

<!-- https://mvnrepository.com/artifact/org.lionsoul/ip2region --><dependency><groupId>org.lionsoul</groupId><artifactId>ip2region</artifactId><version>2.7.0</version></dependency> 地址&#xff1a;http…

【计算机网络】1.5 分组交换网中的时延、丢包和吞吐量

A.分组交换网中的时延 当分组从一个节点沿着路径到后一节点时&#xff0c;该分组在沿途的各个节点经受了几种不同类型的时延。 时延的类型 处理时延 - d n o d a l d_{nodal} dnodal​ 处理时延包括以下部分—— a. 检查分组首部 b. 决定分组导向 排队时延 - d p r o c d_{…

算法基础杂项

目录 1算法最优解 2.时间复杂度排序 3.对数器 1算法最优解 1.首先&#xff0c;保证时间复杂度最低 2.其次&#xff0c;保证空间复杂度最低 3.常数项低不低&#xff0c;一般没人管 2.时间复杂度排序 3.对数器 import java.util.Arrays;public class Test {public static …

KY210 排序

描述&#xff1a; 对输入的n个数进行排序并输出。 输入描述&#xff1a; 输入的第一行包括一个整数n(1<n<100)。 接下来的一行包括n个整数。 输出描述&#xff1a; 可能有多组测试数据&#xff0c;对于每组数据&#xff0c;将排序后的n个整数输出&#xff0c;每个数后面都…

Elasticsearch使用Kibana进行基础操作

一、Restful接口 Elasticsearch通过RESTful接口提供与其进行交互的方式。在ES中&#xff0c;提供了功能丰富的RESTful API的操作&#xff0c;包括CRUD、创建索引、删除索引等操作。你可以用你最喜爱的 web 客户端访问 Elasticsearch 。事实上&#xff0c;你甚至可以使用 curl …

Autoware.auto源码安装

自 2022 年以来&#xff0c;已将 Autoware 的开发迁移到 GitHub。目前Auto版本并没有进行最新维护 一、官网 Autoware.Auto (autowarefoundation.gitlab.io) 二、介绍 Autoware是世界上第一个由Autoware基金会托管的用于自动驾驶汽车的“一体化”开源软件。基于 ROS 2 的 Au…

【25届秋招备战C++】题型练习-背包问题

【25届秋招备战C】题型练习-背包问题 0-1背包416 - 分割等和子集1049 - 最后一块石头的重量 Ⅱ494 - 目标和474- 一和零 完全背包518- 零钱兑换Ⅱ377- 组合总数Ⅱ322- 零钱兑换279- 完全平方数 参考 0-1背包 416 - 分割等和子集 链接: 分割等和子集 解题思路&#xff1a;给定…

单片机中的几种周期(振动/时钟,状态,机械,指令周期)表示的含义(51为例)

几种周期含义及个人理解描述 参考&#xff1a;短文&#xff0c;参考&#xff0c;百度 个人理解简述&#xff1a;对于几个周期性来说&#xff0c;可以认为是小单位的时间组合成了长时间。就像把一个数据赋值&#xff0c;这个是简单的一个机械周期能完成的动作&#xff0c;但需要…

删除远程的master分支,并创建dev分支,将代码推送到dev分支上

在执行这些操作之前&#xff0c;确保你对远程仓库有足够的权限来删除分支和推送代码。下面的步骤将指导你如何删除远程的master分支&#xff0c;创建一个新的dev分支&#xff0c;并将代码推送到这个新分支上。 1. 删除远程的master分支 首先&#xff0c;使用以下命令删除远程…

C语言例:表达式(a=2,3),a+1的值

题目&#xff1a;设int a; 则表达式(a2,3),a1的值 #include<stdio.h> int main(void) {int a0;int b;int c;b (a2,4);c (a2,3),a1;printf("a1%d\n",a1); //a1 3;printf("a2,4的值为&#xff1a;%d\n",b); //a2,4的值为&…

开源模型应用落地-业务优化篇(八)

一、前言 在之前的学习中&#xff0c;我相信您已经学会了一些优化技巧&#xff0c;比如分布式锁、线程池优化、请求排队、服务实例扩容和消息解耦等等。现在&#xff0c;我要给您介绍最后一篇业务优化的内容了。这个优化方法是通过定时统计问题的请求频率&#xff0c;然后将一些…

【进阶五】Python实现SDVRP(需求拆分)常见求解算法——蚁群算法(ACO)

基于python语言&#xff0c;采用经典遗传算法&#xff08;ACO&#xff09;对 需求拆分车辆路径规划问题&#xff08;SDVRP&#xff09; 进行求解。 目录 往期优质资源1. 适用场景2. 代码调整3. 求解结果4. 代码片段参考 往期优质资源 经过一年多的创作&#xff0c;目前已经成熟…

Go函数全景:从基础到高阶的深度探索

目录 一、Go函数基础1.1 函数定义和声明基础函数结构返回值类型和命名返回值 1.2 参数传递方式值传递引用传递 二、Go特殊函数类型2.1 变参函数定义和使用变参变参的限制 2.2 匿名函数与Lambda表达式何为匿名函数Lambda表达式的使用场景 2.3 延迟调用函数&#xff08;defer&…

Arcgis新建位置分配求解最佳商店位置

背景 借用Arcgis帮助文档中的说明:在本练习中,您将为连锁零售店选择可以获得最大业务量的商店位置。主要目标是要将商店定位在人口集中地区附近,因为这种区域对商店的需求量较大。设立这一目标的前提是假设人们往往更多光顾附近的商店,而对于距离较远的商店则较少光顾。您…