C++设计模式——Interpreter解释器模式

news2024/11/25 13:01:37

一,解释器模式的定义

解释器模式是一种行为型设计模式,它用于定义一个语言的文法并解析语言中的表达式,使开发者可以实现自定义语言,并通过解释器对语言中的表达式进行解析和执行。

解释器模式主要用于解决对特定表达式的解析与执行,它定义了一种解释器的语法规则,通过解释器来解析并执行自定义的语言表达式。

通过解释器,可以将复杂的语言解析问题拆分为简单的表达式解析,并且可以通过增加新的解释器对象来扩展语言的功能。

解释器模式在现实生活中的抽象实例:

自然语言解释器:比如翻译软件,可以将一种语言的句子解释为另一种语言。

数学表达式解析器:计算机程序中的数学表达式可以由解析器解释和计算。

正则表达式解析器:正则表达式是一种用于描述字符串模式的语言,正则表达式解析器可以解析正则表达式。

指令解释器:计算机程序中的指令集可以由解释器解释和执行,将指令转换为对计算机底层的操作。

二,解释器模式的结构

解释器模式主要包含以下组件:

1.抽象表达式(Abstract Expression):

定义解释器的接口,声明抽象的解释方法interpret()。

2.终结符表达式(Terminal Expression):

实现抽象表达式的interpret()方法,用于解析语言中的终结符表达式。

3.非终结符表达式(Nonterminal Expression):

实现抽象表达式的interpret()方法,用于解析语言中的非终结符表达式。非终结符表达式通常由多个终结符表达式组成,可以通过递归的方式进行解析。

4.上下文(Context):

包含解释器需要解析的数据,用于存储需要被解释器解析的上下文信息。

5.客户端(Client):

负责创建抽象表达式及具体表达式对象,并调用解释方法进行解析。

在解释器的工作过程中,从输入语言文本开始,解析器首先识别出终结符,然后递归处理它们之间的嵌套结构,直到整个语言表达式都被处理完毕。

解释器模式的关键成分:

终结符表达式(Terminal Expression):

终结符表达式是语法中的基本单元,它们通常是程序的最原子的成分,如数字、关键字、标识符等。终结符表达式的处理相对简单,通常由解释器直接处理并返回结果,在编码实现中,它们对应于语法解析树中的叶子节点。

非终结符表达式(Non-Terminal Expression):

非终结符表达式则代表了更复杂的语法结构,比如判断条件、循环等,它是通过组合其他终结符和非终结符构建而成的。非终结符表达式在解释器模式中通常映射到具体的抽象类或接口,其作用是在适当的时候递归地调用解析器去处理它的组成成分。当遇到非终结符时,解释器会创建一个新的实例,并调用对应的解析方法,这个方法会根据上下文做进一步的解析。

组件之间的工作步骤如下:

1.客户端创建上下文对象,并将待解析的语言表达式传入到上下文。

2.客户端创建终结符表达式和非终结符表达式对象,并根据解释器语法规则将它们组合成一个语法解析树。

3.客户端调用解释器的interpret()方法,依次对每个终结符和非终结符进行解释和执行。

4.解释器根据具体的语法规则对语言表达式进行解析,并执行相应的操作。

5.整个执行流程中,解释器可以将解析得到的值存储在上下文中,以供后续的解析使用。

对应UML类图:

三,解释器模式代码样例

Demo1:解析数学表达式

#include <iostream>
#include <string>
#include <stack>
#include <sstream>

class Expression {
public:
    virtual int interpret() = 0;
};

class NumberExpression : public Expression {
public:
    NumberExpression(int number) : m_number(number) {}
    int interpret() override {
        return m_number;
    }
private:
    int m_number;
};

class AddExpression : public Expression {
public:
    AddExpression(Expression* left, Expression* right)
        : m_left(left), m_right(right) {}
    int interpret() override {
        return m_left->interpret() + m_right->interpret();
    }
private:
    Expression* m_left;
    Expression* m_right;
};

class Context {
public:
    Expression* evaluate(const std::string& expression) {
        std::stack<Expression*> stack;
        std::stringstream ss(expression);
        std::string token;
        while (getline(ss, token, ' ')) {
            if (token == "+") {
                Expression* right = stack.top();
                stack.pop();
                Expression* left = stack.top();
                stack.pop();
                Expression* addExp = new AddExpression(left, right);
                stack.push(addExp);
            }
            else {
                int number = stoi(token);
                Expression* numberExp = new NumberExpression(number);
                stack.push(numberExp);
            }
        }
        return stack.top();
    }
};

int main() {
    Context context;

    Expression* expression = context.evaluate("2 3 +");
    int result = expression->interpret();
    std::cout << "Result: " << result << std::endl;

    delete expression;
    return 0;
}

运行结果:

Result: 5

Demo2:解析字符串

#include <iostream>
#include <string>
#include <vector>

class Expression {
public:
    virtual bool interpret(const std::string& context) = 0;
};

class TerminalExpression : public Expression{
private:
    std::string expression;
public:
    TerminalExpression(const std::string& expression) : expression(expression) {}
    bool interpret(const std::string& context) override {
        if (context.find(expression) != std::string::npos) {
            return true;
        }
        return false;
    }
};

class OrExpression: public Expression {
private:
    Expression* expr1;
    Expression* expr2;
public:
    OrExpression(Expression* expr1, Expression* expr2)
        : expr1(expr1), expr2(expr2) {}
    bool interpret(const std::string& context) override {
        return expr1->interpret(context) || expr2->interpret(context);
    }
};

int main() {
    Expression* expr1 = new TerminalExpression("hello");
    Expression* expr2 = new TerminalExpression("world");
    Expression* orExpr = new OrExpression(expr1, expr2);

    std::vector<std::string> contexts = { "hello", "world", "hello world", "hi" };

    for (const auto& context: contexts) {
        if (orExpr->interpret(context)) {
            std::cout << "Expression is true for context: " << context << std::endl;
        }
        else {
            std::cout << "Expression is false for context: " << context << std::endl;
        }
    }

    delete expr1;
    delete expr2;
    delete orExpr;
    return 0;
}

运行结果:

Expression is true for context: hello
Expression is true for context: world
Expression is true for context: hello world
Expression is false for context: hi

四,解释器模式的应用场景

脚本解析器:如JavaScript、Python等动态语言中,解释器模式用于读取源码并逐行解释执行,不需要预先编译。

数据处理:在数据分析或科学计算库(如NumPy)中,可以使用解释器模式处理复杂的数学表达式。

命令行工具:许多命令行工具会采用解释器模式来解析用户输入的命令及其参数,然后执行相应的操作。

特定描述语言(DSL):比如SQL解析器,将SQL语句转换为数据库操作指令。

五,解释器模式的优缺点

解释器模式的优点:

灵活可扩展,解释器可以动态地添加新的语言规则,这使得系统对新语言或方言的变化更具适应性。

结构清晰,通过一步步执行解析过程,帮助开发者理解并调试。

解释器模式将语法解析和业务逻辑分离,可独立维护。

解释器模式的缺点:

性能开销大,同为语法解析,解释器通常比编译器生成的机器码运行速度慢。

当需要处理高级语法时,代码复杂度很高。

可能会包含重复计算的场景,容易消耗系统资源。

六,代码实战

Demo1:模拟的科学计算库

#include <iostream>
#include <string>

class Expression {
public:
    virtual double evaluate() = 0;
};

class Addition: public Expression {
private:
    double left;
    double right;
public:
    Addition(double l, double r) : left(l), right(r) {}
    double evaluate() override { return left + right; }
};

class Subtraction: public Expression {
private:
    double left;
    double right;
public:
    Subtraction(double l, double r) : left(l), right(r) {}
    double evaluate() override { return left - right; }
};

class Interpreter {
private:
    Expression* expr;
public:
    Interpreter(Expression* e) : expr(e) {}
    double interpret() {
        return expr->evaluate();
    }
};

int main() {
    Expression* addExpr = new Addition(3.0, 5.0);
    Expression* subExpr = new Subtraction(10.0, 7.0);

    Interpreter intp(addExpr);
    std::cout << "Addition result: "<< intp.interpret() << std::endl;
    Interpreter intp2(subExpr);
    std::cout << "Subtraction result: "<< intp2.interpret() << std::endl;

    delete addExpr;
    delete subExpr;
    return 0;
}

运行结果:

Addition result: 8
Subtraction result: 3

Demo2:模拟的汇编语言解析器

#include <iostream>
#include <string>
#include <unordered_map>

class Instruction {
public:
    virtual void execute() const = 0;
};

class LoadInstruction: public Instruction {
private:
    int value;
public:
    LoadInstruction(int v): value(v) {}
    void execute() const override {
        std::cout << "Loading value: " << value << std::endl;
    }
};

class StoreInstruction: public Instruction {
private:
    int address;
public:
    StoreInstruction(int addr) : address(addr) {}
    void execute() const override {
        std::cout << "Storing at address: " << address << std::endl;
    }
};

class Interpreter {
private:
    std::vector<Instruction*> instructions;
public:
    void addInstruction(Instruction* instr) {
        instructions.push_back(instr);
    }
    void interpret() {
        for (auto& instr : instructions) {
            instr->execute();
        }
    }
};

int main() {
    Interpreter interpreter;

    LoadInstruction loadInstr(5);
    StoreInstruction storeInstr(10);

    interpreter.addInstruction(&loadInstr);
    interpreter.addInstruction(&storeInstr);

    interpreter.interpret();
    return 0;
}

运行结果:

Loading value: 5
Storing at address: 10

Demo3:模拟的shell脚本解析器

#include <iostream>
#include <string>
#include <map>

class Command {
public:
    virtual ~Command() {}
    virtual void execute() = 0;
};

class CDCommand: public Command {
private:
    std::string directory;
public:
    CDCommand(const std::string& dir) : directory(dir) {}
    void execute() override {
        std::cout << "Changing directory to " << directory << std::endl;
    }
};

class LSCommand: public Command {
public:
    void execute() override {
        std::cout << "Listing files in current directory\n";
    }
};

class EchoCommand: public Command {
private:
    std::string message;
public:
    EchoCommand(const std::string& msg) : message(msg) {}
    void execute() override {
        std::cout << "Echoing: " << message << "\n";
    }
};

class Interpreter {
private:
    std::map<std::string, Command*> command_map;
public:
    void add_command(const std::string& name, Command* cmd) {
        command_map[name] = cmd;
    }
    void interpret(const std::string& cmd_str) {
        if (command_map.find(cmd_str) != command_map.end()) {
            command_map[cmd_str]->execute();
        }
        else {
            std::cerr << "Unknown command: " << cmd_str << '\n';
        }
    }
};

int main() {
    Interpreter interpreter;

    interpreter.add_command("cd", new CDCommand("/path/to/directory"));
    interpreter.add_command("ls", new LSCommand());
    interpreter.add_command("echo", new EchoCommand("Hello, World!"));

    interpreter.interpret("cd /home");
    interpreter.interpret("ls");      
    interpreter.interpret("echo hi");  

    return 0;
}

运行结果:

Unknown command: cd /home
Listing files in current directory
Unknown command: echo hi

七,参考阅读

https://blog.gmem.cc/interpreter-pattern

https://www.geeksforgeeks.org/interpreter-design-pattern/

https://www.tutorialspoint.com/design_pattern/interpreter_pattern.html

https://incusdata.com/blog/design-patterns-interpreter-pattern-part1

https://sourcemaking.com/design_patterns/interpreter/cpp/

https://mcturra2000.wordpress.com/2017/08/24/a-simple-basic-interpreter-in-c/

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

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

相关文章

python-游戏自动化(三)(实战-豆腐女孩)

前提准备 特别注意&#xff1a; 本节教程所演示的模拟器分辨率设置为 720x1080&#xff08;手机版&#xff09;&#xff0c;电脑分辨率设置大720x1080并且没有设置放大。 今天的课程开始之前我们来回顾一下昨天所学的知识内容&#xff0c;因为今天要学的内容和昨天内容…

CogView-3-Plus:深度解锁智谱AI的图像生成新力量

一、引言&#xff1a;AI助力创意与效率的全面提升 在如今这个瞬息万变的科技时代&#xff0c;AI大模型早就不是实验室里的“神秘武器”&#xff0c;它们已经实实在在地融入到我们的日常工作中了&#xff0c;尤其是在图像生成和内容创作这块儿&#xff0c;简直是效率神器。只要几…

Leetcode3270. 求出数字答案

Every day a Leetcode 题目来源&#xff1a;3270. 求出数字答案 解法1&#xff1a;模拟 按题意模拟。 代码&#xff1a; /** lc appleetcode.cn id3270 langcpp** [3270] 求出数字答案*/// lc codestart class Solution { public:int generateKey(int num1, int num2, int…

基于python+django+vue鲜花商城系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于pythondjangovueMySQL的线…

反射(java)

一、junit单元测试框架 1、单元测试 就是针对最小的功能单元&#xff08;方法&#xff09;&#xff0c;编写测试代码对其进行正确性测试。 之前的是如何进行单元测试的&#xff1f; 有啥问题&#xff1f; 1、只能在ma方法编写测试代码&#xff0c;去调用其他方法进行测试。…

CCS811二氧化碳传感器详解(STM32)

目录 一、介绍 二、传感器原理 1.原理图 2.引脚描述 3.工作原理介绍 三、程序设计 main.c文件 ccs811.h文件 ccs811.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 CCS811模块是一种气体传感器&#xff0c;可以测量环境中TVOC(总挥发性有机物质)浓度和eCO2…

OPPO 全家桶:Find X8/Pro、Pad3 Pro与Enco X3即将10月登场

随着科技的不断发展&#xff0c;智能手机、平板电脑和耳机等电子产品已经成为我们日常生活中不可或缺的一部分。 作为全球知名的科技企业&#xff0c;OPPO一直致力于为消费者提供优质的产品和服务。 近日&#xff0c;有关OPPO全家桶将在10月份“凑齐”的消息引起了广泛关注。…

Elemnt-UI + 递归组件实现后台管理系统左侧菜单

Elemnt-UI 递归组件实现后台管理系统左侧菜单 在 Vue.js 中&#xff0c;允许你编写一个组件来表示一个节点&#xff0c;而这个节点可以包含多个子节点&#xff0c;每个子节点又可以是同样的组件。这种方式使得组件能够处理无限层级的嵌套结构。 应用场景 递归组件非常适合处…

2013年

B D B C D 分支结点是非叶结点 B 47 C A C C D D C A C

2010-2022年各省乡村振兴新质生产力相关变量数据(40+指标)

2010-2022年各省乡村振兴新质生产力相关变量数据&#xff08;40指标&#xff09; 1、时间&#xff1a;2010-2022年 2、来源&#xff1a;统计年鉴、能源统计年鉴、农村统计年鉴、人口和就业统计年鉴、城乡建设统计年鉴以及各省份统计年鉴 3、指标&#xff1a;省份、年份、分地…

信号量(二值信号量和计数信号量)和互斥量

信号量 信号量&#xff08;Semaphore&#xff09; 是一种实现任务间通信的机制&#xff0c; 可以实现任务之间同步或临界资源的互斥访问&#xff0c; 常用于协助一组相互竞争的任务来访问临界资源。 在多任务系统中&#xff0c; 各任务之间需要同步或互斥实现临界资源的保护&a…

图神经网络介绍3

1. 图同构网络&#xff1a;Weisfeiler-Lehman 测试与图神经网络的表达力 本节介绍一个关于图神经网络表达力的经典工作&#xff0c;以及随之产生的另一个重要的模型——图同构网络。图同构问题指的是验证两个图在拓扑结构上是否相同。Weisfeiler-Lehman 测试是一种有效的检验两…

第二期: 第二节 , 逻辑编程 , gpio

1 首先就是 看原理图&#xff1a; 这里有两个 &#xff2c;&#xff25;&#xff24; 核心板的原理图。 可以看到 是这个脚。 &#xff12; 然后就是 查看数据手册。 从 数据手册可以看出 &#xff0c;一共有这么多的 gpio 组&#xff0c; 但是这些 组 是有复用的&#xf…

多文件编程实现链表创建,插入,输出(上)

linklist.c #include "linklist.h" //创建空的链表&#xff0c;为头结点在堆区分配空间 linklist_t *creat_empty_linklist() {linklist_t *head NULL;head (linklist_t *) malloc(sizeof(linknode_t));if(NULL head){printf("malloc is fail!\n");ret…

网格参数的应用和数学基础

引言 对于任意两个拓扑结构相似的表面&#xff0c;可以计算它们之间的一一对应映射。如果其中一个表面由三角形网格表示&#xff0c;那么计算这种映射的问题被称为网格参数化。映射到的表面通常被称为参数域。表面网格与各种域之间的参数化在计算机图形学和几何处理中有广泛的应…

移动WEB开发(第二天)_flex布局

移动WEB开发&#xff08;第二天&#xff09;_flex布局 移动web开发——flex布局1.0传统布局和flex布局对比1.1传统布局1.2 flex布局1.3 建议 2.0 flex布局原理3.0 父项常见属性3.1 flex-direction设置主轴的方向3.2 justify-content 设置主轴上的子元素排列方式3.3 flex-wrap设…

9月美联储决策前哨战——美国CPI数据来袭

随着本周关键CPI数据的即将发布&#xff0c;市场正翘首以待&#xff0c;这将是美联储在9月17日至18日议息会议前获取的最后一块重要经济拼图。鉴于美联储官员已进入传统的政策静默期&#xff0c;8月份的CPI报告无疑将成为交易员们评估未来货币政策走向的重要标尺。 欧洲央行降…

python列表判断是否为空的三种方式

#列表是否为空判断 a[]一&#xff1a; if a:print(not null) else:print(null)二&#xff1a; b len(a) if b 0:print(null) else:print(not null)三&#xff1a; if not a:print(null) else:print(not null)运行结果&#xff1a;

Day9 | Java框架 | SpringBoot

Day9 | Java框架 | SpringBoot SpringBoot简介入门程序概述起步依赖 基础配置配置文件格式&#xff1a;3种yaml语法规则yaml数据读取三种格式 多环境启动配置文件参数命令行参数多环境开发控制&#xff1a;Maven & SpringBoot 多环境兼容 配置文件分类&#xff1a;4种 整合…

Qt+FFmpeg开发视频播放器笔记(三):音视频流解析封装

音频解析 音频解码是指将压缩的音频数据转换为可以再生的PCM(脉冲编码调制)数据的过程。 FFmpeg音频解码的基本步骤如下: 初始化FFmpeg解码器(4.0版本后可省略): 调用av_register_all()初始化编解码器。 调用avcodec_register_all()注册所有编解码器。 打开输入的音频流:…