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

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

相关推荐
Golinie1 小时前
【C++高并发服务器WebServer】-7:共享内存
linux·服务器·c++·webserver
容沁风5 小时前
Qt6+libonvif从大华相机抓拍图片
c++·qt6·海康威视·大华·snap
CHANG_THE_WORLD7 小时前
C++并发编程指南08
开发语言·c++·算法
黎明blooms8 小时前
编译原理之基于自动机的词法分析器的设计与实现
c++·算法·编译原理
bawangtianzun9 小时前
vector有用的自己不太熟悉的函数
c++·算法
Ciderw9 小时前
Go的垃圾回收(GC)机制
开发语言·c++·后端·面试·golang·gc
荣--9 小时前
回顾我的软件开发经历:开发DAB
c++·单元测试·项目管理·nodejs·dab·多平台支持·正交性原则
CodeWizard~9 小时前
原码、反码、补码以及lowbit运算
c语言·开发语言·c++·算法
Icomi_10 小时前
【PyTorch】6.张量运算函数:一键开启!PyTorch 张量函数的宝藏工厂
c语言·c++·人工智能·pytorch·python·深度学习·机器学习
mofei1213812 小时前
Python设计模式 - 组合模式
python·设计模式·组合模式