Update README.md
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
## Updata
|
||||
更新反三角函数的定义域判定,取整函数floor、ceil和round和阶乘运算的支持,同时更新变量暂存计算
|
||||
更新反三角函数的定义域判定,取整函数floor、ceil以及round和阶乘运算的支持,同时更新变量暂存计算
|
||||
|
||||
# 数据结构课设
|
||||
|
||||
|
||||
@@ -1,16 +1,35 @@
|
||||
#include "Calc.h"
|
||||
#include <iostream>
|
||||
#include<algorithm>
|
||||
|
||||
// 静态成员初始化
|
||||
Tokenizer Calc::tokenizer;
|
||||
InfixToPostfix Calc::converter;
|
||||
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) {
|
||||
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> postfix = converter.run(tokens);
|
||||
return evaluator.run(postfix);
|
||||
return evaluator.run(postfix,vars);
|
||||
}
|
||||
|
||||
// 安全计算接口:带 try-catch 封装,返回 bool 标志
|
||||
@@ -42,3 +61,18 @@ void Calc::debug(const string& expr) {
|
||||
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 InfixToPostfix converter;
|
||||
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
|
||||
|
||||
@@ -14,15 +14,20 @@ vector<Token> InfixToPostfix::run(const vector<Token>& infix) {
|
||||
//函数压入运算符栈(函数优先级最高)
|
||||
opStack.push(token);
|
||||
else if (token.is_ope()) {
|
||||
//遇到运算符时,弹出栈中优先级更高或相同且结合性为左结合的运算符
|
||||
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();
|
||||
if (token.value == "!")
|
||||
//后缀操作符直接入栈,不与前面的运算符进行比较
|
||||
opStack.push(token);
|
||||
else {
|
||||
//遇到普通运算符时,弹出栈中优先级更高或相同且结合性为左结合的运算符
|
||||
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())
|
||||
//遇到左括号,直接入栈
|
||||
|
||||
@@ -24,15 +24,25 @@ double PostfixEval::run(const std::vector<Token>& postfix, const unordered_map<s
|
||||
}
|
||||
else stk.push(it->second);
|
||||
}
|
||||
else if (token.is_ope()) {
|
||||
//取出两个操作数,执行二元运算
|
||||
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_ope()) {
|
||||
if (token.value == "!") {
|
||||
if (stk.empty())
|
||||
throw runtime_error("阶乘参数不足");
|
||||
double x = stk.top(); stk.pop();
|
||||
stk.push(apply_Fac(x));
|
||||
}
|
||||
else {
|
||||
//取出两个操作数,执行二元运算
|
||||
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()) {
|
||||
//取出一个操作数,执行一元基本初等函数的计算
|
||||
@@ -63,9 +73,16 @@ double PostfixEval::apply_BO(const string& op, double a, double b) {
|
||||
}
|
||||
if (op == "^") return pow(a, b);
|
||||
if (op == "%") {
|
||||
if (b == 0) throw runtime_error("模除数不能为零");
|
||||
if (b == 0)
|
||||
throw runtime_error("模除数不能为零");
|
||||
return std::fmod(a, b);
|
||||
}
|
||||
if (op == "//") {
|
||||
if (b == 0)
|
||||
throw runtime_error("整除除数不能为零");
|
||||
return std::floor(a / b); // 向下取整整除
|
||||
}
|
||||
|
||||
throw runtime_error("未知运算符:" + op);
|
||||
}
|
||||
|
||||
@@ -87,11 +104,41 @@ double PostfixEval::apply_UF(const string& func, double x) {
|
||||
throw runtime_error("log参数必须大于0");
|
||||
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 == "abs") return fabs(x);
|
||||
if (f == "asin") return asin(x);
|
||||
if (f == "acos") return acos(x);
|
||||
if (f == "asin") {
|
||||
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 == "floor") return std::floor(x); // 向下取整
|
||||
if (f == "ceil") return std::ceil(x); // 向上取整
|
||||
if (f == "round") return std::round(x); // 四舍五入
|
||||
|
||||
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
|
||||
double apply_UF(const string& func, double x);
|
||||
|
||||
//应用于一元后缀运算符,如!
|
||||
double apply_Fac(double x);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,27 +5,29 @@
|
||||
using namespace std;
|
||||
|
||||
vector<Token> Tokenizer::run(const string& expr) {
|
||||
expression = expr;
|
||||
pos = 0;
|
||||
vector<Token> tokens;
|
||||
|
||||
expression = expr;
|
||||
pos = 0;
|
||||
vector<Token> tokens;
|
||||
|
||||
// 主循环:逐字符读取表达式
|
||||
while (pos < expression.length()) {
|
||||
|
||||
skipWhitespace();// 跳过空白字符
|
||||
if (pos >= expression.length()) break;
|
||||
|
||||
char current = expression[pos];
|
||||
|
||||
// 如果是数字或一元负号开头,读取完整数字
|
||||
if (is_dig(current) || (current == '-' && is_UMC(tokens)))
|
||||
if (is_dig(current) || (current == '-' && is_UMC(tokens)))
|
||||
tokens.push_back(readNumber());
|
||||
|
||||
// 如果是字母,读取函数或变量名
|
||||
else if (is_let(current))
|
||||
else if (is_let(current))
|
||||
tokens.push_back(readIdentifier());
|
||||
|
||||
// 否则读取操作符或括号
|
||||
else
|
||||
else
|
||||
tokens.push_back(readOperatorOrParen());
|
||||
}
|
||||
|
||||
@@ -69,13 +71,18 @@ Token Tokenizer::readIdentifier() {
|
||||
|
||||
//识别函数与变量(若后面紧跟‘(’则为函数,否则为变量)
|
||||
skipWhitespace();
|
||||
if (pos < expression.length() && expression[pos] == '(')
|
||||
if (pos < expression.length() && expression[pos] == '(')
|
||||
return Token(TokenType::Function, name);
|
||||
else
|
||||
else
|
||||
return Token(TokenType::Variable, name);
|
||||
}
|
||||
|
||||
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++]; // 读取当前字符并自增指针
|
||||
|
||||
// 判断字符类型并创建相应 Token
|
||||
@@ -84,7 +91,9 @@ Token Tokenizer::readOperatorOrParen() {
|
||||
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, "^", 3, Associativity::Right);
|
||||
case '!': return Token(TokenType::Operator, "!", 4, Associativity::Right);
|
||||
case '(': return Token(TokenType::LeftParen, "(");
|
||||
case ')': return Token(TokenType::RightParen, ")");
|
||||
default:
|
||||
@@ -111,5 +120,5 @@ bool Tokenizer::is_let(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