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. 将表达式拆分为子表达式,并按照语法规则解析。

完整代码实现

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

cpp 复制代码
#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
cpp 复制代码
class Expression {
public:
    virtual int interpret() const = 0;
    virtual ~Expression() = default;
};
  • 定义了解释器的通用接口。
  • interpret()方法返回当前表达式的值。

2. 终结符表达式类(NumberExpression
cpp 复制代码
class NumberExpression : public Expression {
private:
    int number;

public:
    explicit NumberExpression(int value) : number(value) {}

    int interpret() const override {
        return number;
    }
};
  • 表示数字。
  • interpret()方法直接返回数字值。

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

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

解释器模式的应用场景

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

总结

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

:对于复杂的语法规则,可能会导致大量的类和对象,增加系统复杂性。

  1. 适用领域有限:适合简单的语法,复杂语言的解释器更适合通过生成器工具实现(如ANTLR)。

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

相关推荐
杨筱毅1 小时前
【优秀三方库研读】【C++基础知识】odygrd/quill -- 折叠表达式
c++·三方库研读
东阳马生架构1 小时前
Sentinel源码—8.限流算法和设计模式总结二
算法·设计模式·sentinel
hjjdebug2 小时前
c++中的enum变量 和 constexpr说明符
c++·enum·constexpr
CoderCodingNo2 小时前
【GESP】C++二级真题 luogu-B4259 [GESP202503 二级] 等差矩阵
java·c++·矩阵
明月看潮生2 小时前
青少年编程与数学 02-018 C++数据结构与算法 11课题、分治
c++·算法·青少年编程·编程与数学
冰茶_3 小时前
C#中常见的设计模式
java·开发语言·microsoft·设计模式·微软·c#·命令模式
Echo``3 小时前
2:QT联合HALCON编程—图像显示放大缩小
开发语言·c++·图像处理·qt·算法
Niuguangshuo3 小时前
Python 设计模式:访问者模式
python·设计模式·访问者模式
不当菜虚困4 小时前
JAVA设计模式——(七)代理模式
java·设计模式·代理模式
想睡hhh4 小时前
c++STL——stack、queue、priority_queue的模拟实现
开发语言·c++·stl