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

news2024/10/7 8:28:10

【声明】本题目来源于卡码网(卡码网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;
    }
    @Override
    public 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;
    }
    @Override
    public 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.coloradmin.cn/o/1423547.html

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

相关文章

【INTEL(ALTERA)】带有浮点单元 (FPU) Nios® V/g 处理器在 英特尔® Cyclone10 GX 设备中执行不正确的浮点运算

说明 由于 英特尔 Quartus Prime Pro Edition 软件版本 23.3 存在一个问题&#xff0c;当使用 Nios V/g 处理器并在 英特尔 Cyclone 10 GX 设备中启用 FPU 时&#xff0c;浮点运算无法按预期进行。 Nios V/g 处理器 – 启用浮点单元 解决方法 请勿在 英特尔 CycloneNios 10 G…

张维迎《博弈与社会》笔记(2)导论:个体理性与社会最优:协调与合作问题

有节选&#xff0c;相当于按照自己的方式将内容组织了下吧&#xff1f; 协调与合作问题 什么是一个社会面临的基本问题&#xff1f; 这似乎是一个我们每一个人都可以触摸得到但又难以说清的问题&#xff0c;因为在不同的语境下&#xff0c;“社会”一词的内涵有所差异。但其基…

火山引擎ByteHouse:分析型数据库如何设计并发控制?

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 分析型数据库设计并发控制的主要原因是为了确保数据的完整性和一致性&#xff0c;同时提高数据库的吞吐量和响应速度。并发控制可以防止多个事务同时对同一数据进行…

ICMPv6报文解析及NAT处理

ICMPv6报文概述 参考RFC4443和RFC2460 ICMPv6报文是IPv6在internal control management protocol&#xff08;ICMP&#xff09;的基础之上做了一些改动&#xff0c;得到了ICMPv6协议&#xff0c;IPv6的next_header为58。 Message general format 每条ICMPv6消息之前都有一个…

从零开始的OpenGL光栅化渲染器构建6-PBR光照模型

前言 PBR&#xff0c;或者基于物理的渲染(Physically Based Rendering)&#xff0c;它指的是一些在不同程度上都基于与现实世界的物理原理更相符的基本理论所构成的渲染技术的集合。正因为基于物理的渲染目的便是为了使用一种更符合物理学规律的方式来模拟光线&#xff0c;因此…

Mybatis 源码系列:领略设计模式在 Mybatis 其中的应用

文章目录 一、Builder模式二、工厂模式三、单例模式四、代理模式五、组合模式六、模板方式模式七、适配器模式八、装饰器模式九、迭代器模式 虽然我们都知道有23种设计模式&#xff0c;但是大多停留在概念层面&#xff0c;真实开发中很少遇到&#xff0c;Mybatis源码中使用了大…

代理IP技术在云函数中的创新应用与拓展空间

目录 前言 一、代理IP技术的基本概念和原理 二、云函数的基本原理和优势 1. 弹性伸缩 2. 省时省力 3. 按需计费 三、代理IP技术在云函数中的创新应用 1. 反爬虫技术 2. 访问安全性和隐私保护 3. 地理定位和访问控制 四、代理IP技术在云函数中的拓展空间 1. 代理IP池…

力扣hot100 跳跃游戏 贪心

Problem: 55. 跳跃游戏 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 参考 挨着跳&#xff0c;记录最远能到达的地方 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1) Code class Solution {public boolean canJump(int[] nums)…

AI大模型专题:大模型安全与伦理研究报告2024

今天分享的是AI大模型系列深度研究报告&#xff1a;《AI大模型专题&#xff1a;大模型安全与伦理研究报告2024》。 &#xff08;报告出品方&#xff1a;腾讯研究院&#xff09; 报告共计&#xff1a;76页 大模型具有不同于传统模型的特点和优势 第一&#xff0c;大模型和传统…

页面通过Vue进行整体页面不同语言切换 i18n库

目录 引入 如何做到 下载i18n库 构建整体翻译文件结构 语言包文件 i18n配置文件 把i18n挂载到vue实例上 添加按钮点击事件切换语言 引入 我们现在有这样一个要求,我们想要对我们开发的网页进行国际化操作,也就是我们不仅要有中文,还要有英文等。用户可以随时进行不同语言…

langchain+xray:prompt控制漏洞扫描

写在前面 xray是长亭推出的一款漏洞扫描工具。 langchain是调用LLM大模型完成自动化任务的框架。 本篇文章是对langchain自定义工具的探索&#xff0c;通过编写一个xray调用的工具&#xff0c;联合ChatGPT对xray进行调用&#xff0c;实现对目标的漏洞扫描。 xray功能分析 …

洛谷-P4124题-手机号码-Java

题目 题目链接&#xff1a; https://www.luogu.com.cn/problem/P4124 分析 给定两个长度为11位的数字&#xff0c;代表两个区间 [L,R] 需要编写程序来计算出&#xff0c;这两个区间内满足要求的数字个数。这样的题一般来说就是数位dp题。首先我们可以根据容斥原理 [0,R]中满…

第一批 Apple Vision Pro 开箱和佩戴体验新鲜出炉!!!

注: 本文转自微信公众号 BravoAI (专注AI资讯和技术分享), 原文网址:第一批 Apple Vision Pro 开箱和佩戴体验新鲜出炉!!!, 扫码关注公众号 编者按: 整个AR/VR行业都在等AVP, 期待它能带来ChatGPT般的冲击 AVP(Apple Vision Pro) 是苹果公司研发的第一款"空间计算 (Spa…

跟着pink老师前端入门教程-day11

3、CSS 三角 网页中常见一些三角形&#xff0c;使用CSS直接画出来即可&#xff0c;不必做成图片或字体图标 4、CSS 用户界面样式 4.1 什么是界面样式 界面样式&#xff1a;更改一些用户操作样式&#xff0c;以便提高更好的用户体验&#xff08;更改用户的鼠标样式、表单轮廓…

Redis简单阐述、安装配置及远程访问

目录 一、Redis简介 1.什么是Redis 2.特点 3.优势 4.数据库对比 5.应用场景 二、 安装与配置 1.下载 2.上传解压 3.安装gcc 4.编译 5.查看安装目录 6.后端启动 7.测试 8.系统服务配置 三、Redis远程访问 1.修改访问IP地址 2.设置登录密码 3.重启Redis服务 …

《QDebug 2024年1月》

一、Qt Widgets 问题交流 1. 二、Qt Quick 问题交流 1.Repeator 的 delegate 在 remove 移除时的注意事项 Qt Bug Tracker&#xff1a;https://bugreports.qt.io/browse/QTBUG-47500 Repeator 在调用 remove 函数之后&#xff0c;对应的 Item 会立即释放&#xff0c;后续就…

RabbitMQ之死信队列、延迟队列和懒队列

目录 死信队列 何时会产生死信 死信队列的配置方式 参数x-dead-letter-routing-key 如何确定一个消息是不是死信 延迟队列 懒队列 声明懒队列的两种方式 参数声明 策略指定 死信队列 死信队列是RabbitMQ中非常重要的一个特性。简单理解&#xff0c;他是RabbitMQ对于未…

第九节HarmonyOS 常用基础组件20-Divider

1、描述 提供分割器组件&#xff0c;分割不同内容块或内容元素。 2、接口 Divider() 3、属性 名称 参数类型 描述 vertical boolean 使用水平分割线还是垂直分割线。 false&#xff1a;水平分割线 true&#xff1a;垂直分割线 color ResourceColor 分割线颜色 默认…

数字图像处理(实践篇)三十七 OpenCV-Python 使用SIFT和BFmatcher对两个输入图像的关键点进行匹配实践

目录 一 涉及的函数 二 实践 三 报错处理 使用SIFT(尺度不变特征变换)算法

【Midjourney】AI绘画案例(1)龙年吉祥神兽

说明&#xff1a; 1、文中图片版权均为Midjourney所有&#xff0c;请勿用作商业用途。 2、文中图片均经过 Upscale x 4 处理。 3、由于模型原因&#xff0c;某些图片存在暇玼。 1、吉祥神兽——天马&#xff08;独角兽&#xff09; 天马消灾星。 提示词 Prompt: Sky Unicor…