C++ 设计模式之解释器模式

【声明】本题目来源于卡码网(卡码网KamaCoder)

【提示:如果不想看文字介绍,可以直接跳转到C++编码部分


设计模式大纲】


【简介】

        --什么是解释器模式(第22种设计模式)

        解释器模式(Interpreter Pattern)是⼀种行为型设计模式,它定义了⼀个语⾔的⽂法,并且建⽴⼀个【解释器】来解释该语⾔中的句子。
        比如说SQL语法、正则表达式,这些内容比较简短,但是表达的内容可不仅仅是字⾯上的那些符号,计算机想要理解这些语法,就需要解释这个语法规则,因此解释器模式常⽤于实现编程语⾔解释器、正则表达式处理等场景。


【基本结构】

        解释器模式主要包含以下⼏个角色:

  • 1. 抽象表达式(Abstract Expression): 定义了解释器的接口,包含了解释器的⽅法 interpret 。
  • 2. 终结符表达式(Terminal Expression): 在语法中不能再分解为更⼩单元的符号。
  • 3. 非终结符表达式(Non-terminal Expression): 文法中的复杂表达式,它由终结符和其他⾮终结符组成。
  • 4. 上下文(Context): 包含解释器之外的⼀些全局信息,可以存储解释器中间结果,也可以⽤于向解释器传递信息。

举例来说,表达式 "3 + 5 * 2",数字 "3" 和 "5", "2" 是终结符,⽽运算符 "+", "*"都需要两个操作数, 属于⾮终结符。

 


 【简易实现-Java】

1. 创建抽象表达式接口:

        定义解释器的接⼝,声明⼀个 interpret ⽅法,用于解释语⾔中的表达式。

// 抽象表达式接⼝
public interface Expression {int interpret();
}

2. 创建具体的表达式类:

         实现抽象表达式接口,用于表示语⾔中的具体表达式。

public class TerminalExpression implements Expression {private int value;public TerminalExpression(int value) {this.value = value;}@Overridepublic int interpret() {return value;}
}

3. 非终结符表达式:

        抽象表达式的⼀种,⽤于表示语⾔中的⾮终结符表达式,通常包含其他表达式。

public class AddExpression implements Expression {private Expression left;private Expression right;public AddExpression(Expression left, Expression right) {this.left = left;this.right = right;}@Overridepublic int interpret() {return left.interpret() + right.interpret();}
}

4. 上下文:

        包含解释器需要的⼀些全局信息或状态。

public class Context {// 可以在上下⽂中存储⼀些全局信息或状态
}

5. 客户端:

        构建并组合表达式,然后解释表达式。

public class Main {public static void main(String[] args) {Context context = new Context();Expression expression = new AddExpression(new TerminalExpression(1),new TerminalExpression(2));int result = expression.interpret();System.out.println("Result: " + result);}
}

【使用场景】

        当需要解释和执⾏特定领域或业务规则的语⾔时,可以使用解释器模式。例如,SQL解释器、正则表达式解释器等。但是需要注意的是解释器模式可能会导致类的层次结构较为复杂,同时也可能不够灵活,使用要慎重。


【C++编码部分】

1. 题目描述

        小明正在设计一个计算器,用于解释用户输入的简单数学表达式,每个表达式都是由整数、加法操作符+、乘法操作符组成的,表达式中的元素之间用空格分隔,请你使用解释器模式帮他实现这个系统。

2. 输入描述

        每行包含一个数学表达式,表达式中包含整数、加法操作符(+)和乘法操作符(*)。 表达式中的元素之间用空格分隔。

3. 输出描述

        对于每个输入的数学表达式,每行输出一个整数,表示对应表达式的计算结果。

4. C++编码示例

注意: 其中的解析表达式函数比较抽象,但不是解释器模式的核心,无需太过关注,只需要了解到这种模式的特定即可。

/**
* @version Copyright (c) 2024 NCDC, Servo。 Unpublished - All rights reserved
* @file InterpreterMode.hpp
* @brief 解释器模式
* @autor 写代码的小恐龙er
* @date 2024/01/26
*/#include <iostream>
#include <string>
#include <stack>
#include <vector>using namespace std;// 前置声明//  表达式 接口类
class Expression;
// 终结符表达式类 -- 数字
class NumberExpression;
// 非终结符表达式 -- 加法运算符
class AddExpression;
// 非终结符表达式 -- 乘法运算符
class MultiplyExpression;
// 上下文类
class Context;// 类的定义//  表达式 接口类
class Expression
{
public:virtual int Interpret() = 0;virtual string ReturnType() = 0;
};// 终结符表达式类 -- 数字
class NumberExpression : public Expression
{
private:int _number;
public:NumberExpression(int number){this->_number = number;}// 重载接口函数int Interpret() override{return _number;}string ReturnType() override{return "number";}
};// 非终结符表达式 -- 加法运算符
class AddExpression : public Expression
{
private:Expression * _left;Expression * _right;
public:AddExpression(){}AddExpression(Expression * left, Expression * right){this->_left = left;this->_right = right;}// 重载接口函数int Interpret() override{return _left->Interpret() + _right->Interpret();}string ReturnType() override{return "+";}
};// 非终结符表达式 -- 乘法运算符
class MultiplyExpression : public Expression
{
private:Expression * _left;Expression * _right;
public:MultiplyExpression(){}MultiplyExpression(Expression * left, Expression * right){this->_left = left;this->_right = right;}// 重载接口函数int Interpret() override{return _left->Interpret() * _right->Interpret();}string ReturnType() override{return "*";}
};// 上下文类
class Context
{
private:stack<Expression *> _expressionSt;
public:void PushExpression(Expression * expression){_expressionSt.push(expression);}void PopExpression(){_expressionSt.pop();}
};// 解析输入的表达式 【有点复杂 -- 花了一个多小时想出来的】
Expression *ParseExpression(string input){// 存放 表达式类vector<Expression *> result;// 存放 加号表达式 【消除 乘号表达式】stack<Expression *> resultAdd;int i = 0;// 本次时间复杂度为 O(n)的操作为 寻找出所有的 表达式类while(i < (int)input.size()){string number = "";char temp = input[i];while(temp >= '0' && temp <= '9'){number += input[i++];temp = input[i];if(temp == ' ' || temp == '\0'){result.push_back(new NumberExpression((int)atoi(number.c_str())));}}if(temp == '+'){result.push_back(new AddExpression());}else if(temp == '*'){result.push_back(new MultiplyExpression());}i++;}// 由于 表达式 均是 数字 + 符号 的连接形式 // 则 我们可以 先将 所有的乘号 消除 转换为Multiplyexpression// 最后将所有的 + 转换为-个AddExpression;// 这个for 循环 找出所有乘号 时间复杂度 O(n)int size = result.size();for(i = 0; i < size; i++){if(result[i]  && result[i]->ReturnType() == "*"){if(i - 1 >= 0 && i + 1 < size){// 这行代码的目的是将 其中一个加数 pop出去 变成 乘数if(!resultAdd.empty()) resultAdd.pop();resultAdd.push(new MultiplyExpression(result[i - 1], result[i + 1]));result[i] = nullptr;i++;result[i] = nullptr;}else return nullptr;}else if(result[i]  && (result[i]->ReturnType() == "number" || result[i]->ReturnType() == "+")){resultAdd.push(result[i]);}}// 本次操作为 将所有的 加法 转换为 一个 表达式类 while(resultAdd.size() >= 3){Expression *left = resultAdd.top();resultAdd.pop();Expression *add = resultAdd.top();resultAdd.pop();Expression *right = resultAdd.top();resultAdd.pop();// 将 加号 运算符 添加进 栈add = new AddExpression(left, right);resultAdd.push(add);if(resultAdd.size() < 3) break;}return resultAdd.top();
}// 在vs2022里面可以正常运行
// 测试用例: 
// 2 + 3
// 2 * 3
// 2 + 2 * 3
// 2 * 3 + 2
// 2 + 2 * 3 + 1
// 2 * 3 + 1 + 3 * 2
int main()
{// 新建上下文类Context *context = new Context();// 输入的表达式string input = "";while(std::cin >> input){// 表达式解析Expression *expression = ParseExpression(input);if(expression){// 通过上下文类来保存一些关键信息context->PushExpression(expression);// 通过一个 表达式 解析后的 唯一表达式来进行原酸std::cout << expression->Interpret() << endl;}else std::cout << "Invalid expression." << endl;}delete context;context = nullptr;return 0;
}


 ......

To be continued.

【再有最后一种设计模式了! 下午更新】

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

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

相关文章

CodeGPT

GitCode - 开发者的代码家园 gitcode.com/ inscode.csdn.net/liujiaping/java_1706242128563/edit?openFileMain.java&editTypelite marketplace.visualstudio.com/items?itemNameCSDN.csdn-codegpt&spm1018.2226.3001.9836&extra%5Butm_source%5Dvip_chatgpt_c…

实力上榜!安全狗入选《CCSIP 2023中国网络安全行业业全景册(第六版)》多个细项

1月24日&#xff0c;Freebuf发布了《CCSIP 2023中国网络安全行业业全景册&#xff08;第六版&#xff09;》。 作为国内云原生安全领导厂商&#xff0c;安全狗也入选多个细分领域。 厦门服云信息科技有限公司&#xff08;品牌名&#xff1a;安全狗&#xff09;创办于2013年&…

学习gin框架知识的小注意点

Gin框架的初始化 有些项目中 初始化gin框架写的是&#xff1a; r : gin.New() r.Use(logger.GinLogger(), logger.GinRecovery(true)) 而不是r : gin.Default() 为什么呢&#xff1f; 点击进入Default源码发现其实他也是new两个中间件&#xff0c;&#xff08;Logger&…

【并发编程】锁死的问题——如何解决?以及如何避免?

目录 1.如何解决 一、死锁的定义和原因 1.1 定义 1.2 原因 二、常见的死锁场景 2.1 线程间相互等待资源 2.2 嵌套锁的循环等待 2.3 对资源的有序请求 三、死锁排查的方法 3.1 使用jstack命令 3.2 使用jconsole 3.3 使用VisualVM 四、常见的解决方案 4.1 避免嵌套锁…

STK姿态分析(一)矢量组件

文章目录 内容简介一、卫星矢量二、卫星坐标平面三、卫星姿态球面 – 内容简介 接下来一系列文章将进行STK目标&#xff08;卫星、导弹、火箭、飞机、船舶&#xff09;姿态分析的仿真。本篇将使用矢量&#xff08;vector&#xff09;组件对卫星姿态、传感器指向等进行3D可视化…

注册表学习——注册表结构

简介&#xff1a;注册表是由很多项和值构成的。 HEKY_USERS&#xff08;HKU&#xff09; 主要保存默认用户及当前登录用户配置信息。 .DEFAULT 该项是针对未来创建的新用户所保存的默认配置项。 S-1-5-18等项 这些项叫作安全标识符&#xff08;SID&#xff09;用来表示Windows操…

Linux(linux版本 centos 7) 下安装 oracle 19c详细教程(新手小白易上手)

一、安装前准备 1、下载预安装包 wget http://yum.oracle.com/repo/OracleLinux/OL7/latest/x86_64/getPackage/oracle-database-preinstall-19c-1.0-1.el7.x86_64.rpm预安装包下载成功 2、下载oracle安装包 下载地址如下 https://www.oracle.com/cn/database/technologies…

渲染与创造之美:互为表里的艺术

在五彩斑斓的艺术世界中&#xff0c;渲染与创造是两股不可或缺的力量。它们之间的关系&#xff0c;恰如弓与箭&#xff0c;互为表里&#xff0c;共同塑造出无数令人叹为观止的视觉景象。创造之美是指通过创新思维和创造力&#xff0c;将想象具象化为现实&#xff0c;创造出新的…

引领未来:云原生在产品、架构与商业模式中的创新与应用

文章目录 一、云原生产品创新二、云原生架构设计三、云原生商业模式变革《云原生落地 产品、架构与商业模式》适读人群编辑推荐内容简介目录 随着云计算技术的不断发展&#xff0c;云原生已经成为企业数字化转型的重要方向。接下来将从产品、架构和商业模式三个方面&#xff0c…

最小覆盖子串(Leetcode76)

例题&#xff1a; 分析: 比如现在有字符串&#xff08;s&#xff09;&#xff0c;s "ADOBECODEBANC", 给出目标字符串 t "ABC", 题目就是要从原始字符串&#xff08;s&#xff09;中找到一个子串&#xff08;res&#xff09;可以覆盖目标字符串 t &…

微信小程序(十六)slot插槽

注释很详细&#xff0c;直接上代码 上一篇 温馨提醒&#xff1a;此篇需要自定义组件的基础&#xff0c;如果不清楚请先看上一篇 新增内容&#xff1a; 1.单个插槽 2.多个插槽 单个插糟 源码&#xff1a; myNav.wxml <view class"navigationBar custom-class">…

师如灯塔,照我前行:我在誉天的RHCA认证之旅

时光荏苒&#xff0c;岁月如梭。2022年10月&#xff0c;我踏上了通向RHCA&#xff08;Red Hat Certified Architect&#xff09;证书的征程。2023年11月&#xff0c;我成功拿到了RHCA证书&#xff0c;也给这段旅程画上了圆满的句号。 而在这充满挑战和成长的旅程中&#xff0c;…

防火墙ip配置

如图所示需要配置该拓扑的ip 1.首先在交换机7上创建vlan 2 3 [Huanwei]vlan batch 2 3 2.分别进入0/0/3 和0/0/2接口 [Huawei-GigabitEthernet0/0/3]port link-type access [Huawei-GigabitEthernet0/0/2]port link-type access 3.定义所属vlan [Huawei-GigabitEthernet0…

【python爬虫】爬虫编程技术的解密与实战

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a; 爬虫】网络爬虫探秘⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 目录 &#x1f33c;实验目的 &#x1f…

数字孪生系统的难点

数字孪生系统的开发和实施涉及一些技术难点&#xff0c;这些难点需要综合应用多个领域的知识和技术来克服。以下是一些数字孪生系统开发中的技术难点&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1…

【每日一题】4.LeetCode——杨辉三角

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢迎各位大佬指点&…

监听元素宽高变化---new ResizeObserver

参考&#xff1a;ResizeObserver API详解-CSDN博客 有的时候需要监听某个元素的宽高变化&#xff0c;这个时候可以使用JS的 resizeObserver 钩子函数。 用于监视元素的大小变化。它可以观察一个或多个 DOM 元素&#xff0c;以便在元素的大小或形状发生变化时触发回调函数。R…

安全用电管理平台方案介绍——Acrelcloud-6000

安全用电管理平台是一个针对电力系统安全管理的平台&#xff0c;旨在提供对电力设备和用电行为进行监控、分析和管理的解决方案。该平台结合了物联网技术、数据分析和远程监控等技术手段&#xff0c;能够实时监测、分析和预警电力系统的安全状况&#xff0c;以便及时采取措施防…

广州工业元宇宙赋能新型工业化,推动工业制造业数字化转型发展

随着科技的飞速发展&#xff0c;新型工业化的概念逐渐成为全球关注的焦点。在数字化转型的浪潮中&#xff0c;工业制造业的发展面临着巨大的机遇和挑战。广州作为中国南方的重要工业基地&#xff0c;积极探索工业元宇宙的赋能作用&#xff0c;以推动工业制造业的数字化转型发展…

[蓝桥学习] 前缀和与差分

前缀和原理 特点 求区间和 如果要实现一边修改一边查询&#xff0c;需要使用树状树组和线段树。 例题 题目很简单&#xff0c;但是代码实现惊艳到我了&#xff0c;是L就加1&#xff0c;是Q就减1&#xff0c;如果区间 [i,j] 是平衡子串的话&#xff0c;那它会在前缀prefix i …