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)。

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

相关推荐
Summer_Xu21 分钟前
模拟 Koa 中间件机制与洋葱模型
前端·设计模式·node.js
车载小杜2 小时前
基于指针的线程池
开发语言·c++
云徒川2 小时前
【设计模式】原型模式
java·设计模式·原型模式
云 无 心 以 出 岫4 小时前
贪心算法QwQ
数据结构·c++·算法·贪心算法
换一颗红豆5 小时前
【C++ 多态】—— 礼器九鼎,釉下乾坤,多态中的 “风水寻龙诀“
c++
随便昵称5 小时前
蓝桥杯专项复习——前缀和和差分
c++·算法·前缀和·蓝桥杯
commonbelive5 小时前
团体程序设计天梯赛——L1-100 四项全能
c++
genispan5 小时前
QT/C++ 多线程并发下载实践
开发语言·c++·qt
小卡皮巴拉6 小时前
【力扣刷题实战】矩阵区域和
开发语言·c++·算法·leetcode·前缀和·矩阵
Pacify_The_North6 小时前
【C++进阶三】vector深度剖析(迭代器失效和深浅拷贝)
开发语言·c++·windows·visualstudio