一、解释器模式
解释器模式(Interpreter Pattern)是一种行为型设计模式,主要用于解析和解释特定的语言或表达式。它的核心思想是为语言中的每种语法规则定义一个解释器,通过这些解释器将语言的表示形式转换为可执行的操作。解释器模式在实际业务中使用频率相对较低,因为其性能和类膨胀的问题,需求较少。在处理解析和解释特定的语言或类似表达式转换或者算术表达式计算等复杂计算逻辑的场景,这种模式常见。
解释器模式的结构图
抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作。
终结符表达式(Terminal Expression)角色:实现文法中与终结符相关的操作。
非终结符表达式(Nonterminal Expression)角色:实现文法中与非终结符相关的操作。
环境(Context)角色:包含解释器需要的数据或是公共的功能。
客户端(Client):主要任务是将需要分析的句子或表达式转换成抽象语法树,然后调用解释器的解释方法。
解释器模式涉及三个关键的元素
抽象语法树:在解释器模式中,语言的句子通常表示为抽象语法树(AST)。AST是句子的树形表示,它反映了句子的结构,使得解释器可以按照树的结构来解释句子。
文法与句子:文法是用于描述语言的语法结构的形式规则。句子是语言的基本单位,由终结符构成,能由“文法”推导出。
解释器:解释器是使用解释器模式设计的类,它用于解释和执行语言的句子。
二、解释器模式的设计方法
设计代码实现一个简单的算术表达式解释器,能够处理加法和减法操作,并且可以从终端读取表达式进行求值。终端输入表达式格式:[数字][空格][操作符][空格][数字]
interpreter.cpp
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <stack>
#include <cctype>
#include <stdexcept>
// 接口
class Expression {
public:
virtual ~Expression() {}
virtual int interpreter() const = 0;
};
// 数值表达式
class NumberExpression : public Expression {
public:
NumberExpression(int value) : value_(value) {}
int interpreter() const override {
return value_;
}
private:
int value_;
};
// 加法表达式
class AdditionExpression : public Expression {
public:
AdditionExpression(Expression* left, Expression* right) : left_(left), right_(right) {}
int interpreter() const override {
return left_->interpreter() + right_->interpreter();
}
~AdditionExpression() {
delete left_;
delete right_;
}
private:
Expression* left_;
Expression* right_;
};
// 减法表达式
class SubtractionExpression : public Expression {
public:
SubtractionExpression(Expression* left, Expression* right) : left_(left), right_(right) {}
int interpreter() const override {
return left_->interpreter() - right_->interpreter();
}
~SubtractionExpression() {
delete left_;
delete right_;
}
private:
Expression* left_;
Expression* right_;
};
class ExpressionEvaluator {
public:
static int evaluate(const std::vector<std::string>& tokens) {
std::stack<Expression*> values;
std::stack<std::string> operators;
for (const auto& token : tokens) {
if (isdigit(token[0])) {
values.push(new NumberExpression(std::stoi(token)));
} else if (token == "+" || token == "-") {
while (!operators.empty() && (precedence(operators.top()) >= precedence(token))) {
processOPerator(values,operators);
}
operators.push(token);
} else {
throw std::runtime_error("Syntax error: 无效的符号 " + token);
}
}
while (!operators.empty()) {
processOPerator(values,operators);
}
if (values.size() != 1) {
throw std::runtime_error("Syntax error: 无效的算术表达式");
}
int result = values.top()->interpreter();
delete values.top();
return result;
}
private:
static int precedence(const std::string& operate) {
if (operate == "+" || operate == "-") return 1;
return 0;
}
static void processOPerator(std::stack<Expression*>& values, std::stack<std::string>&operators) {
if (values.size() <2) {
throw std::runtime_error("Syntax error: 操作数不足");
}
Expression* right = values.top(); values.pop();
Expression* left = values.top(); values.pop();
std::string operate = operators.top(); operators.pop();
Expression* result = nullptr;
if (operate == "+") {
result = new AdditionExpression(left, right);
} else if (operate == "-") {
result = new SubtractionExpression(left, right);
} else {
throw std::runtime_error("Syntax error: 无效的操作符 " + operate);
}
values.push(result);
}
};
// 客户端
int main() {
std::cout << "\n// 示例用法1 " << std::endl;
std::cout << "终端输入 '3 + 4 - 2' " << std::endl;
std::vector<std::string> tokens = {"3", "+", "4", "-", "2"};
try {
int result = ExpressionEvaluator::evaluate(tokens);
std::cout << "算术表达式结果: " << result << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
std::cout << "\n// 示例用法2 " << std::endl;
std::cout << "终端输入 '10 + 20 - 30 + 40' " << std::endl;
tokens = {"10", "+", "20", "-", "30", "+", "40"};
try {
int result = ExpressionEvaluator::evaluate(tokens);
std::cout << "算术表达式结果: " << result << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
tokens.clear();
std::string line;
std::cout << "\n请输入算术表达式 (例如:'3 + 4 - 2'): ";
std::getline(std::cin, line);
std::istringstream iss(line);
std::string token;
while (iss >> token) {
if (token != "+" && token != "-" && !isdigit(token[0])) {
std::cerr << "Error: 无效的符号 " << token << std::endl;
return 1;
}
tokens.push_back(token);
}
try {
int result = ExpressionEvaluator::evaluate(tokens);
std::cout << "算术表达式结果: " << result << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
代码中使用了中缀表示法(即常见的 3 - 4 + 5 这种形式),并且利用了栈来处理操作符和操作数。需要注意的是数字包括符号之间需要有空格。(空格作为切割符)
运行效果
三、解释器模式的应用场景
编程语言的解释器:解释器模式常用于构建编程语言的解释器。例如,CPython、V8,你也可以设计一个自定义的小型编程语言,通过定义解释器模式中的表达式和语法规则来解析和执行这些语言。
复杂的计算器:当你需要构建一个可以解析并计算复杂数学表达式的计算器时,解释器模式可以帮助将数学表达式分解为更简单的操作,然后逐步计算结果,例如上面的代码实现中interpreter.cpp。
规则引擎:在某些应用场景中,需要根据特定的规则或条件执行不同的操作。解释器模式可以用来实现这些规则的解析和执行,例如业务规则引擎。
DSL(领域特定语言):如果你需要为特定领域创建一个定制的语言或语法(例如用于配置或查询),解释器模式可以帮助你解析这种领域特定语言的表达式,并执行相应的操作。
数据处理和转换:在需要将输入数据转换为特定格式的场景中,解释器模式可以帮助将数据解析成中间表示,然后进行转换和处理。
脚本语言:当需要实现一个小型脚本语言,或在应用中嵌入一个脚本解释器时,解释器模式提供了一种组织和解释脚本语言的方式。
四、总结
解释器模式允许你可以将复杂的语句表达式转换成对应的执行代码。这种模式通常用于字符串解析或语言解释器中。它将算术或逻辑表达式设计成一个接一个的独立的“组件”,每个组件负责解释其中一个语法符号的含义。这些组件可以单独构建、组合或重用。