Update README.md
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
## Updata
|
## Updata
|
||||||
更新反三角函数的定义域判定,取整函数floor、ceil和round和阶乘运算的支持,同时更新变量暂存计算
|
更新反三角函数的定义域判定,取整函数floor、ceil以及round和阶乘运算的支持,同时更新变量暂存计算
|
||||||
|
|
||||||
# 数据结构课设
|
# 数据结构课设
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,35 @@
|
|||||||
#include "Calc.h"
|
#include "Calc.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include<algorithm>
|
||||||
|
|
||||||
// 静态成员初始化
|
// 静态成员初始化
|
||||||
Tokenizer Calc::tokenizer;
|
Tokenizer Calc::tokenizer;
|
||||||
InfixToPostfix Calc::converter;
|
InfixToPostfix Calc::converter;
|
||||||
PostfixEval Calc::evaluator;
|
PostfixEval Calc::evaluator;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, double> Calc::vars = {
|
||||||
|
{"PAI", 2 * std::asin(1)},
|
||||||
|
{"pi", 3.14159265358979},
|
||||||
|
{"e", std::exp(1)}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// 主计算接口:表达式字符串 -> 计算结果
|
// 主计算接口:表达式字符串 -> 计算结果
|
||||||
double Calc::eval(const string& expression) {
|
double Calc::eval(const string& expression) {
|
||||||
|
std::string varName, rightExpr;
|
||||||
|
|
||||||
|
// 判断是否为赋值表达式,例如 x = 3 + sin(2)
|
||||||
|
if (is_assign(expression, varName, rightExpr)) {
|
||||||
|
vector<Token> tokens = tokenizer.run(rightExpr);
|
||||||
|
vector<Token> postfix = converter.run(tokens);
|
||||||
|
double val = evaluator.run(postfix, vars);
|
||||||
|
vars[varName] = val;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
vector<Token> tokens = tokenizer.run(expression);
|
vector<Token> tokens = tokenizer.run(expression);
|
||||||
vector<Token> postfix = converter.run(tokens);
|
vector<Token> postfix = converter.run(tokens);
|
||||||
return evaluator.run(postfix);
|
return evaluator.run(postfix,vars);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 安全计算接口:带 try-catch 封装,返回 bool 标志
|
// 安全计算接口:带 try-catch 封装,返回 bool 标志
|
||||||
@@ -42,3 +61,18 @@ void Calc::debug(const string& expr) {
|
|||||||
cerr << "[Debug Error] " << e.what() << endl;
|
cerr << "[Debug Error] " << e.what() << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Calc::is_assign(const std::string& expr, std::string& varName, std::string& rightExpr) {
|
||||||
|
size_t eqPos = expr.find('=');
|
||||||
|
if (eqPos != std::string::npos) {
|
||||||
|
varName = expr.substr(0, eqPos);
|
||||||
|
rightExpr = expr.substr(eqPos + 1);
|
||||||
|
// 去除空格
|
||||||
|
varName.erase(remove_if(varName.begin(), varName.end(), isspace), varName.end());
|
||||||
|
rightExpr.erase(remove_if(rightExpr.begin(), rightExpr.end(), isspace), rightExpr.end());
|
||||||
|
// 检查变量是否合法(只含字母)
|
||||||
|
return !varName.empty() && std::all_of(varName.begin(), varName.end(), ::isalpha);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ private:
|
|||||||
static Tokenizer tokenizer;
|
static Tokenizer tokenizer;
|
||||||
static InfixToPostfix converter;
|
static InfixToPostfix converter;
|
||||||
static PostfixEval evaluator;
|
static PostfixEval evaluator;
|
||||||
|
|
||||||
|
//变量表
|
||||||
|
static std::unordered_map<std::string, double> vars;
|
||||||
|
//赋值判断和处理函数
|
||||||
|
static bool is_assign(const std::string& expr, std::string& varName, std::string& rightExpr);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -14,15 +14,20 @@ vector<Token> InfixToPostfix::run(const vector<Token>& infix) {
|
|||||||
//函数压入运算符栈(函数优先级最高)
|
//函数压入运算符栈(函数优先级最高)
|
||||||
opStack.push(token);
|
opStack.push(token);
|
||||||
else if (token.is_ope()) {
|
else if (token.is_ope()) {
|
||||||
//遇到运算符时,弹出栈中优先级更高或相同且结合性为左结合的运算符
|
if (token.value == "!")
|
||||||
while (!opStack.empty() && opStack.top().is_ope() &&
|
//后缀操作符直接入栈,不与前面的运算符进行比较
|
||||||
(precedence(opStack.top()) > precedence(token) ||
|
opStack.push(token);
|
||||||
(precedence(opStack.top()) == precedence(token) && associativity(token) == Associativity::Left))) {
|
else {
|
||||||
output.push_back(opStack.top());
|
//遇到普通运算符时,弹出栈中优先级更高或相同且结合性为左结合的运算符
|
||||||
opStack.pop();
|
while (!opStack.empty() && opStack.top().is_ope() &&
|
||||||
|
(precedence(opStack.top()) > precedence(token) ||
|
||||||
|
(precedence(opStack.top()) == precedence(token) && associativity(token) == Associativity::Left))) {
|
||||||
|
output.push_back(opStack.top());
|
||||||
|
opStack.pop();
|
||||||
|
}
|
||||||
|
//当前运算符入栈
|
||||||
|
opStack.push(token);
|
||||||
}
|
}
|
||||||
//当前运算符入栈
|
|
||||||
opStack.push(token);
|
|
||||||
}
|
}
|
||||||
else if (token.is_LP())
|
else if (token.is_LP())
|
||||||
//遇到左括号,直接入栈
|
//遇到左括号,直接入栈
|
||||||
|
|||||||
@@ -25,14 +25,24 @@ double PostfixEval::run(const std::vector<Token>& postfix, const unordered_map<s
|
|||||||
else stk.push(it->second);
|
else stk.push(it->second);
|
||||||
}
|
}
|
||||||
else if (token.is_ope()) {
|
else if (token.is_ope()) {
|
||||||
//取出两个操作数,执行二元运算
|
if (token.value == "!") {
|
||||||
if (stk.size() < 2) throw runtime_error("操作数不足");
|
if (stk.empty())
|
||||||
double b = stk.top();
|
throw runtime_error("阶乘参数不足");
|
||||||
stk.pop();
|
double x = stk.top(); stk.pop();
|
||||||
double a = stk.top();
|
stk.push(apply_Fac(x));
|
||||||
stk.pop();
|
}
|
||||||
double res = apply_BO(token.value, a, b);
|
else {
|
||||||
stk.push(res);
|
//取出两个操作数,执行二元运算
|
||||||
|
if (stk.size() < 2)
|
||||||
|
throw runtime_error("操作数不足");
|
||||||
|
double b = stk.top();
|
||||||
|
stk.pop();
|
||||||
|
double a = stk.top();
|
||||||
|
stk.pop();
|
||||||
|
double res = apply_BO(token.value, a, b);
|
||||||
|
stk.push(res);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (token.is_fun()) {
|
else if (token.is_fun()) {
|
||||||
//取出一个操作数,执行一元基本初等函数的计算
|
//取出一个操作数,执行一元基本初等函数的计算
|
||||||
@@ -63,9 +73,16 @@ double PostfixEval::apply_BO(const string& op, double a, double b) {
|
|||||||
}
|
}
|
||||||
if (op == "^") return pow(a, b);
|
if (op == "^") return pow(a, b);
|
||||||
if (op == "%") {
|
if (op == "%") {
|
||||||
if (b == 0) throw runtime_error("模除数不能为零");
|
if (b == 0)
|
||||||
|
throw runtime_error("模除数不能为零");
|
||||||
return std::fmod(a, b);
|
return std::fmod(a, b);
|
||||||
}
|
}
|
||||||
|
if (op == "//") {
|
||||||
|
if (b == 0)
|
||||||
|
throw runtime_error("整除除数不能为零");
|
||||||
|
return std::floor(a / b); // 向下取整整除
|
||||||
|
}
|
||||||
|
|
||||||
throw runtime_error("未知运算符:" + op);
|
throw runtime_error("未知运算符:" + op);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,11 +104,41 @@ double PostfixEval::apply_UF(const string& func, double x) {
|
|||||||
throw runtime_error("log参数必须大于0");
|
throw runtime_error("log参数必须大于0");
|
||||||
return log(x);
|
return log(x);
|
||||||
}
|
}
|
||||||
|
if (f == "ln") {
|
||||||
|
if(x<=0)
|
||||||
|
throw runtime_error("ln参数必须大于0");
|
||||||
|
return log(x) / log(exp(1));
|
||||||
|
}
|
||||||
if (f == "exp") return exp(x);
|
if (f == "exp") return exp(x);
|
||||||
if (f == "abs") return fabs(x);
|
if (f == "abs") return fabs(x);
|
||||||
if (f == "asin") return asin(x);
|
if (f == "asin") {
|
||||||
if (f == "acos") return acos(x);
|
if (x < -1 || x > 1)
|
||||||
|
throw runtime_error("asin 参数必须在 [-1, 1] 范围内");
|
||||||
|
return std::asin(x);
|
||||||
|
}
|
||||||
|
if (f == "acos") {
|
||||||
|
if (x < -1 || x > 1)
|
||||||
|
throw runtime_error("asin 参数必须在 [-1, 1] 范围内");
|
||||||
|
return std::acos(x);
|
||||||
|
}
|
||||||
if (f == "atan") return atan(x);
|
if (f == "atan") return atan(x);
|
||||||
|
|
||||||
|
if (f == "floor") return std::floor(x); // 向下取整
|
||||||
|
if (f == "ceil") return std::ceil(x); // 向上取整
|
||||||
|
if (f == "round") return std::round(x); // 四舍五入
|
||||||
|
|
||||||
throw runtime_error("未知函数:" + func);
|
throw runtime_error("未知函数:" + func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double PostfixEval::apply_Fac(double x) {
|
||||||
|
if (x < 0)
|
||||||
|
throw runtime_error("负数不能求阶乘");
|
||||||
|
if (x != floor(x))
|
||||||
|
throw runtime_error("阶乘参数必须为整数");
|
||||||
|
|
||||||
|
int n = static_cast<int>(x);
|
||||||
|
double result = 1;
|
||||||
|
for (int i = 2; i <= n; ++i)
|
||||||
|
result *= i;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ private:
|
|||||||
|
|
||||||
//应用于医院基本初等函数,如sin, cos, log
|
//应用于医院基本初等函数,如sin, cos, log
|
||||||
double apply_UF(const string& func, double x);
|
double apply_UF(const string& func, double x);
|
||||||
|
|
||||||
|
//应用于一元后缀运算符,如!
|
||||||
|
double apply_Fac(double x);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,12 +5,14 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
vector<Token> Tokenizer::run(const string& expr) {
|
vector<Token> Tokenizer::run(const string& expr) {
|
||||||
expression = expr;
|
|
||||||
pos = 0;
|
expression = expr;
|
||||||
vector<Token> tokens;
|
pos = 0;
|
||||||
|
vector<Token> tokens;
|
||||||
|
|
||||||
// 主循环:逐字符读取表达式
|
// 主循环:逐字符读取表达式
|
||||||
while (pos < expression.length()) {
|
while (pos < expression.length()) {
|
||||||
|
|
||||||
skipWhitespace();// 跳过空白字符
|
skipWhitespace();// 跳过空白字符
|
||||||
if (pos >= expression.length()) break;
|
if (pos >= expression.length()) break;
|
||||||
|
|
||||||
@@ -76,6 +78,11 @@ Token Tokenizer::readIdentifier() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Token Tokenizer::readOperatorOrParen() {
|
Token Tokenizer::readOperatorOrParen() {
|
||||||
|
if (expression[pos] == '/' && pos + 1 < expression.length() && expression[pos + 1] == '/') {
|
||||||
|
pos += 2; // 跳过两个斜杠
|
||||||
|
return Token(TokenType::Operator, "//", 2, Associativity::Left);
|
||||||
|
}
|
||||||
|
|
||||||
char c = expression[pos++]; // 读取当前字符并自增指针
|
char c = expression[pos++]; // 读取当前字符并自增指针
|
||||||
|
|
||||||
// 判断字符类型并创建相应 Token
|
// 判断字符类型并创建相应 Token
|
||||||
@@ -84,7 +91,9 @@ Token Tokenizer::readOperatorOrParen() {
|
|||||||
case '-': return Token(TokenType::Operator, "-", 1, Associativity::Left);
|
case '-': return Token(TokenType::Operator, "-", 1, Associativity::Left);
|
||||||
case '*': return Token(TokenType::Operator, "*", 2, Associativity::Left);
|
case '*': return Token(TokenType::Operator, "*", 2, Associativity::Left);
|
||||||
case '/': return Token(TokenType::Operator, "/", 2, Associativity::Left);
|
case '/': return Token(TokenType::Operator, "/", 2, Associativity::Left);
|
||||||
|
case '%': return Token(TokenType::Operator, "%", 2, Associativity::Left);
|
||||||
case '^': return Token(TokenType::Operator, "^", 3, Associativity::Right);
|
case '^': return Token(TokenType::Operator, "^", 3, Associativity::Right);
|
||||||
|
case '!': return Token(TokenType::Operator, "!", 4, Associativity::Right);
|
||||||
case '(': return Token(TokenType::LeftParen, "(");
|
case '(': return Token(TokenType::LeftParen, "(");
|
||||||
case ')': return Token(TokenType::RightParen, ")");
|
case ')': return Token(TokenType::RightParen, ")");
|
||||||
default:
|
default:
|
||||||
@@ -111,5 +120,5 @@ bool Tokenizer::is_let(char c) const {
|
|||||||
|
|
||||||
bool Tokenizer::is_OpeCh(char c) const {
|
bool Tokenizer::is_OpeCh(char c) const {
|
||||||
// 判断是否为合法的运算符字符
|
// 判断是否为合法的运算符字符
|
||||||
return c == '+' || c == '-' || c == '*' || c == '/' || c == '^';
|
return c == '+' || c == '-' || c == '*' || c == '/' || c == '^' || c == '%';
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user