From e8aa17224cdbaef7ecbce5c0022ac8168cf7ee3b Mon Sep 17 00:00:00 2001 From: KiriAky107 <2026692687@qq.com> Date: Tue, 1 Jul 2025 16:07:16 +0800 Subject: [PATCH] Create README.md --- README.md | 315 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..6997477 --- /dev/null +++ b/README.md @@ -0,0 +1,315 @@ +# 数据结构课设 + +## ——计算器的双栈类实现 + +## 一、总体架构 + +### 1.数据结构: + +##### 双栈结构(操作数栈+运算符栈) + +操作数栈:用于后缀表达式求值时的数据储存。 + +运算符栈:用于中缀表达式向后缀表达式转换时的运算符存储 + +### 2.基本框架: + +中缀表达式->中缀转后缀->后缀表达式->后缀表达式求值 + +### 3.方法实现: + +#### (1).表达式解析方法(Tokenizer类) + +功能:将字符串形式的数学表达式拆解为基本单元(token),包括数字、运算符、括号、函数名等。 + +输出:Token序列(如["3","+","sin","(","2","*","5",")"]) + +#### (2).表达式转换方法(InfixToPostfix类) + +功能:基于逆波兰表示法,将中缀表达式(带优先级)转换为后缀表达式 + +核心:结合栈实现优先级比较、括号处理、函数名识别 + +输出:后缀表达式序列(如["3","2","5","*","sin","+"]) + +#### (3).后缀表达式求值方法(PostfixEval类) + +功能:使用操作数栈对后缀表达式逐步计算,得出最终结果。 + +支持:基础四则运算,基本初等函数,负号 + +输出:最终计算结果(double类型) + +#### (4).总控协调方法(Calc类) + +功能:统一调度调用表达式解析、转换、求职模块,作为对外唯一调用入口 + +接口:`double eval(string expression)` + +支持debug、异常处理、扩展模块对接等功能 + + + +## 二、具体实现 + +### 0.0需要的STL库头文件 + +```cpp +#include // 标准输入输出(调试、主程序) +#include // 处理字符串表达式 +#include // 中缀转后缀、后缀求值都需用到栈 +#include // 储存 Token 序列 +#include // isdigit, isalpha 等字符判断 +#include // sin, cos, log 等函数计算(用于 PostfixEval) +#include // 抛出非法表达式等异常 +``` + +### 0.1需要的自定义头文件 + +```cpp +#include "Token.h" // Token 结构体 +#include "Tokenizer.h" // 表达式解析类 +#include "InfixToPostfix.h" // 中缀转后缀类 +#include "PostfixEval.h" // 后缀求值类 +#include "Calc.h" // 总控类(对外调用接口) +``` + + + +### 1.Token类 + +类型:数字、操作符、左括号、右括号、函数、变量 + +属性: + +类型(enum) + +内容(string) + +优先级(仅运算符) + +结合性(左/右) + +```cpp +enum class TokenType{ + Number, + Operator, + LeftParen, + RightParen, + Function, + Variable +}; + +enum class Associativity { + Left, + Right, + None +}; + +struct Token { + TokenType type; + std::string value; + + // 以下属性仅对运算符或函数适用 + int precedence = -1; // 运算优先级(* > +) + Associativity associativity = Associativity::None; + + // 构造函数 + Token(TokenType t, const std::string& v, + int p = -1, Associativity assoc = Associativity::None) + : type(t), value(v), precedence(p), associativity(assoc) {} + + // 工具函数:是否为数字 + bool isNumber() const { return type == TokenType::Number; } + + // 是否为操作符 + bool isOperator() const { return type == TokenType::Operator; } + + // 是否为函数 + bool isFunction() const { return type == TokenType::Function; } + + // 是否为左括号 + bool isLeftParen() const { return type == TokenType::LeftParen; } + + // 是否为右括号 + bool isRightParen() const { return type == TokenType::RightParen; } + + // 是否为变量(如 x、y) + bool isVariable() const { return type == TokenType::Variable; } +}; +``` + +### 2.Tokenizer类 + +方法: + +`run(string expr) → vector`:表达式字符串解析主函数 + +`readNumber(string, int&)`:识别多位数字及小数点 + +`readFunction(string, int&)`:识别函数名称如 sin、cos + +`handleUnaryMinus()`:识别一元负号 + +```cpp +class Tokenizer { +public: + // 主函数:输入表达式字符串,输出 token 序列 + std::vector run(const std::string& expr); + +private: + // 当前处理的表达式和指针位置 + std::string expression; + size_t pos = 0; + + // 工具函数:跳过空格 + void skipWhitespace(); + + // 识别多位数字及小数点(支持负数符号) + Token readNumber(); + + // 识别函数名,如 sin, cos, log + Token readFunction(); + + // 识别变量名(可扩展,如 x, y, z) + Token readVariable(); + + // 识别符号、括号、运算符等 + Token readOperatorOrParen(); + + // 判断是否需要处理一元负号 + bool isUnaryMinusContext(const std::vector& tokens); + + // 工具判断函数 + bool isDigit(char c) const; + bool isLetter(char c) const; + bool isOperatorChar(char c) const; +}; + +``` + +### 3.InfixToPostfix 类 + +方法: + +run(vector) → vector:中缀转后缀主函数 + +precedence(Token):判断运算符优先级 + +associativity(Token):左结合/右结合判断 + +popUntilLeftParen():用于处理括号结束时的栈弹出过程 + +```cpp +class InfixToPostfix { +public: + // 主转换函数:将中缀表达式转换为后缀表达式 + std::vector run(const std::vector& infix); + +private: + // 返回指定运算符的优先级 + int precedence(const Token& token) const; + + // 判断运算符的结合性(左结合或右结合) + Associativity associativity(const Token& token) const; + + // 遇到右括号时,从栈中弹出操作符直到遇到左括号 + void popUntilLeftParen(std::stack& opStack, std::vector& output); +}; +``` + +### 4. PostfixEval 类 + +方法: + +`run(vector) → double`:后缀表达式求值` + +`applyBinaryOp(op, a, b)`:执行加减乘除幂 + +`applyUnaryFunction(func, x)`:执行函数(如 sin、sqrt) + +```cpp +class PostfixEval { +public: + // 主函数:对后缀表达式进行求值 + double run(const std::vector& postfix); + +private: + // 应用于二元操作符,如 +, -, *, /, ^ + double applyBinaryOp(const std::string& op, double a, double b); + + // 应用于一元基本初等函数,如 sin, cos, log + double applyUnaryFunction(const std::string& func, double x); +}; +``` + +包含的数学库函数 + +```cpp +sin(); +cos(); +log(); +sqrt(); +exp(); +fabs(); +pow(); +``` + +### 5. Calc 类(对外接口) + +方法: + +`eval(string) → double`:用户主调用入口 + +`debug()`:调试辅助,可打印 token 列表、后缀表达式 + +`safeEval(string) → optional`:异常捕获 + +```cpp +class Calc { +public: + // 主接口函数:输入表达式字符串,返回计算结果 + static double eval(const std::string& expression); + + // 安全接口:带异常处理,返回 optional + static std::optional safeEval(const std::string& expression); + // 调试函数:打印中间的 Token 序列和后缀表达式 + void debug(const std::string& expr); +}; + +``` + +### 6.main方法 + +```c++ +#include +#include +#include "Calc.h" + +int main() { + std::string input; + + std::cout << "欢迎使用科学计算器(支持 + - * / ^ 和基本初等函数)\n"; + std::cout << "输入表达式(输入 q 退出):\n"; + + while (true) { + std::cout << "\n>>> "; + std::getline(std::cin, input); + + if (input == "q" || input == "Q") break; + + try { + double result = Calc::eval(input); + std::cout << "结果 = " << result << "\n"; + } catch (const std::exception& e) { + std::cerr << "错误: " << e.what() << "\n"; + } catch (...) { + std::cerr << "未知错误。\n"; + } + } + + std::cout << "感谢使用,再见!\n"; + return 0; +} +``` +