From 0f8e7d3282fdef550cd25d84d786ea119dd56eb9 Mon Sep 17 00:00:00 2001 From: KiriAky Date: Tue, 1 Jul 2025 16:04:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ScientificCalculator.sln | 31 ++++ ScientificCalculator/Calc.cpp | 44 ++++++ ScientificCalculator/Calc.h | 28 ++++ ScientificCalculator/InfixToPostfix.cpp | 74 +++++++++ ScientificCalculator/InfixToPostfix.h | 27 ++++ ScientificCalculator/PostfixEval.cpp | 97 ++++++++++++ ScientificCalculator/PostfixEval.h | 25 +++ .../ScientificCalculator.vcxproj | 142 ++++++++++++++++++ .../ScientificCalculator.vcxproj.filters | 51 +++++++ ScientificCalculator/Token.h | 52 +++++++ ScientificCalculator/Tokenizer.cpp | 115 ++++++++++++++ ScientificCalculator/Tokenizer.h | 40 +++++ ScientificCalculator/main.cpp | 29 ++++ 13 files changed, 755 insertions(+) create mode 100644 ScientificCalculator.sln create mode 100644 ScientificCalculator/Calc.cpp create mode 100644 ScientificCalculator/Calc.h create mode 100644 ScientificCalculator/InfixToPostfix.cpp create mode 100644 ScientificCalculator/InfixToPostfix.h create mode 100644 ScientificCalculator/PostfixEval.cpp create mode 100644 ScientificCalculator/PostfixEval.h create mode 100644 ScientificCalculator/ScientificCalculator.vcxproj create mode 100644 ScientificCalculator/ScientificCalculator.vcxproj.filters create mode 100644 ScientificCalculator/Token.h create mode 100644 ScientificCalculator/Tokenizer.cpp create mode 100644 ScientificCalculator/Tokenizer.h create mode 100644 ScientificCalculator/main.cpp diff --git a/ScientificCalculator.sln b/ScientificCalculator.sln new file mode 100644 index 0000000..9231925 --- /dev/null +++ b/ScientificCalculator.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36127.28 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScientificCalculator", "ScientificCalculator\ScientificCalculator.vcxproj", "{5C991D9D-797E-4C35-8C97-E6F43099CBD6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Debug|x64.ActiveCfg = Debug|x64 + {5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Debug|x64.Build.0 = Debug|x64 + {5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Debug|x86.ActiveCfg = Debug|Win32 + {5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Debug|x86.Build.0 = Debug|Win32 + {5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Release|x64.ActiveCfg = Release|x64 + {5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Release|x64.Build.0 = Release|x64 + {5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Release|x86.ActiveCfg = Release|Win32 + {5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {227C30D6-2F25-4EA4-B670-3C930B28DA7B} + EndGlobalSection +EndGlobal diff --git a/ScientificCalculator/Calc.cpp b/ScientificCalculator/Calc.cpp new file mode 100644 index 0000000..a50328d --- /dev/null +++ b/ScientificCalculator/Calc.cpp @@ -0,0 +1,44 @@ +#include "Calc.h" +#include + +// ̬Աʼ +Tokenizer Calc::tokenizer; +InfixToPostfix Calc::converter; +PostfixEval Calc::evaluator; + +// ӿڣʽַ -> +double Calc::eval(const string& expression) { + vector tokens = tokenizer.run(expression); + vector postfix = converter.run(tokens); + return evaluator.run(postfix); +} + +// ȫӿڣ try-catch װ bool ־ +bool Calc::safeEval(const string& expr, double& res) { + try { + res = eval(expr); + return true; + } + catch (const exception& e) { + cerr << "[Error] " << e.what() << endl; + return false; + } +} + +// Խӿڣӡмtoken б ׺ʽ +void Calc::debug(const string& expr) { + try { + vector tokens = tokenizer.run(expr); + cout << "[Token List]" << endl; + for (const auto& t : tokens) cout << t.value << " "; + cout << endl; + + vector postfix = converter.run(tokens); + cout << "[Postfix]" << endl; + for (const auto& t : postfix) cout << t.value << " "; + cout << endl; + } + catch (const exception& e) { + cerr << "[Debug Error] " << e.what() << endl; + } +} diff --git a/ScientificCalculator/Calc.h b/ScientificCalculator/Calc.h new file mode 100644 index 0000000..e312bf0 --- /dev/null +++ b/ScientificCalculator/Calc.h @@ -0,0 +1,28 @@ +#ifndef CALC_H +#define CALC_H + +#include"Tokenizer.h" +#include"InfixToPostfix.h" +#include"PostfixEval.h" +#include +#include + +using namespace std; + +class Calc { +public: + //ӿںʽַؼ + static double eval(const string& expresssion); + + //ȫӿڣ쳣optional + static bool safeEval(const string& expr,double& res); + + //Ժӡtokenб׺ʽ + static void debug(const string& expr); +private: + static Tokenizer tokenizer; + static InfixToPostfix converter; + static PostfixEval evaluator; +}; + +#endif diff --git a/ScientificCalculator/InfixToPostfix.cpp b/ScientificCalculator/InfixToPostfix.cpp new file mode 100644 index 0000000..1e59c30 --- /dev/null +++ b/ScientificCalculator/InfixToPostfix.cpp @@ -0,0 +1,74 @@ +#include "InfixToPostfix.h" +#include + +//ʹ沨ʾ׺ʽתΪ׺ʽ +vector InfixToPostfix::run(const vector& infix) { + vector output;// ջ + stack opStack;//׺ʽ + + for (const Token& token : infix) { + if (token.is_num() || token.is_var()) + //ֱֻӷ + output.push_back(token); + else if (token.is_fun()) + //ѹջȼߣ + 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(); + } + //ǰջ + opStack.push(token); + } + else if (token.is_LP()) + //ţֱջ + opStack.push(token); + else if (token.is_RP()) + //ţջֱ + pop_ULP(opStack, output); + } + + //󣬵ջ + while (!opStack.empty()) { + if (opStack.top().is_LP() || opStack.top().is_RP()) + throw runtime_error("Ųƥ");//Ųƥʱ + output.push_back(opStack.top()); + opStack.pop(); + } + + return output;//غ׺ʽ +} + +//ȼԽȼԽ +int InfixToPostfix::precedence(const Token& token) const { + return token.precedence; +} + +//ԣ/ +Associativity InfixToPostfix::associativity(const Token& token) const { + return token.associativity; +} + +//ţջ֪ +void InfixToPostfix::pop_ULP(stack& opStack, vector& output) { + //ֱ + while (!opStack.empty() && !opStack.top().is_LP()) { + output.push_back(opStack.top()); + opStack.pop(); + } + + //Ϊ׳쳣 + if (opStack.empty()) + throw runtime_error("Ųƥ䣺ȱʧ"); + opStack.pop(); // + + //֮ǰǺҲջ + if (!opStack.empty() && opStack.top().is_fun()) { + output.push_back(opStack.top()); + opStack.pop(); + } +} \ No newline at end of file diff --git a/ScientificCalculator/InfixToPostfix.h b/ScientificCalculator/InfixToPostfix.h new file mode 100644 index 0000000..4ad2b91 --- /dev/null +++ b/ScientificCalculator/InfixToPostfix.h @@ -0,0 +1,27 @@ +#ifndef INFIX_TO_POSTFIX_H +#define INFIX_TO_POSTFIX_H + +#include"Token.h" +#include +#include + +using namespace std; + +class InfixToPostfix { +public: + //ת׺ʽתΪ׺ʽ + vector run(const vector& infix); + +private: + //ָȼ + int precedence(const Token& token) const; + + //жĽԣ/ң + Associativity associativity(const Token& token) const; + + //ʱջеֱ + void pop_ULP(stack& opStack, vector& output); + +}; + +#endif diff --git a/ScientificCalculator/PostfixEval.cpp b/ScientificCalculator/PostfixEval.cpp new file mode 100644 index 0000000..cb11863 --- /dev/null +++ b/ScientificCalculator/PostfixEval.cpp @@ -0,0 +1,97 @@ +#include"PostfixEval.h" +#include +#include + +double PostfixEval::run(const std::vector& postfix, const unordered_map& vars) { + stack stk; + + for (const auto& token : postfix) { + if (token.is_num()) + //תΪdoubleջ + stk.push(stod(token.value)); + else if (token.is_var()) { + //ұӳ + auto it = vars.find(token.value); + // ֧ó PAI e + + if (it == vars.end()) { + if (token.value == "PAI") + stk.push(2.0 * std::asin(1.0)); // = 2 * arcsin(1) + else if (token.value == "e") + stk.push(std::exp(1.0)); // e = exp(1) + else + throw runtime_error("δ壺" + token.value); + } + 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_fun()) { + //ȡһִһԪȺļ + if (stk.empty()) throw runtime_error(""); + double x = stk.top(); + stk.pop(); + double res = apply_UF(token.value, x); + stk.push(res); + } + else + throw runtime_error("δ֪Token"); + } + + if (stk.size() != 1) + throw runtime_error("ʽ󣬼ջΨһ"); + + return stk.top(); +} + +//жԪ +double PostfixEval::apply_BO(const string& op, double a, double b) { + if (op == "+") return a + b; + if (op == "-") return a - b; + if (op == "*") return a * b; + if (op == "/") { + if (b == 0) throw runtime_error("Ϊ0"); + return a / b; + } + if (op == "^") return pow(a, b); + if (op == "%") { + if (b == 0) throw runtime_error("ģΪ"); + return std::fmod(a, b); + } + throw runtime_error("δ֪" + op); +} + +//һԪȺ +double PostfixEval::apply_UF(const string& func, double x) { + string f = func; + transform(f.begin(), f.end(), f.begin(), ::tolower); + + if (f == "sin") return sin(x); + if (f == "cos") return cos(x); + if (f == "tan") return tan(x); + if (f == "sqrt") { + if (x < 0) + throw runtime_error("sqrtΪ"); + return sqrt(x); + } + if (f == "log") { + if (x <= 0) + throw runtime_error("log0"); + return log(x); + } + 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 == "atan") return atan(x); + + throw runtime_error("δ֪" + func); +} diff --git a/ScientificCalculator/PostfixEval.h b/ScientificCalculator/PostfixEval.h new file mode 100644 index 0000000..f1525c3 --- /dev/null +++ b/ScientificCalculator/PostfixEval.h @@ -0,0 +1,25 @@ +#ifndef POSTFIXEVAL_H +#define POSYFIXEVAL_H + +#include "Token.h" +#include +#include +#include +#include +#include + +using namespace std; +class PostfixEval { +public: + //Ժ׺ʽֵ + double run(const std::vector& postfix, const unordered_map& vars = {}); + +private: + //ӦڶԪ+-*/^ + double apply_BO(const string& op, double a, double b); + + //ӦҽԺȺsin, cos, log + double apply_UF(const string& func, double x); +}; + +#endif diff --git a/ScientificCalculator/ScientificCalculator.vcxproj b/ScientificCalculator/ScientificCalculator.vcxproj new file mode 100644 index 0000000..cc97c7d --- /dev/null +++ b/ScientificCalculator/ScientificCalculator.vcxproj @@ -0,0 +1,142 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {5c991d9d-797e-4c35-8c97-e6f43099cbd6} + ScientificCalculator + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ScientificCalculator/ScientificCalculator.vcxproj.filters b/ScientificCalculator/ScientificCalculator.vcxproj.filters new file mode 100644 index 0000000..eb51469 --- /dev/null +++ b/ScientificCalculator/ScientificCalculator.vcxproj.filters @@ -0,0 +1,51 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + \ No newline at end of file diff --git a/ScientificCalculator/Token.h b/ScientificCalculator/Token.h new file mode 100644 index 0000000..b030a2f --- /dev/null +++ b/ScientificCalculator/Token.h @@ -0,0 +1,52 @@ +#ifndef TOKEN_H +#define TOKEN_H + +#include + +using namespace std; +//Ǻ +enum class TokenType { + Number, + Operator, + LeftParen, + RightParen, + Function, + Variable +}; + +// +enum class Associativity { + Left, + Right, + None +}; + +//Tokenṹ +struct Token { + TokenType type;//Ǻ + string value; //ʵַ + int precedence = -1; //ȼͺЧ + Associativity associativity = Associativity::None; //ԣͺЧ + + //캯 + Token(TokenType t, const string& v, + int p = -1, Associativity assoc = Associativity::None) + :type(t), value(v), precedence(p), associativity(assoc) {} + + //жϷ + + // жǷΪ֣ "3.14", "42" + bool is_num() const { return type == TokenType::Number; } + // жǷΪ "+", "-", "*", "/" + bool is_ope() const { return type == TokenType::Operator; } + // жǷΪѧ "sin", "cos", "log" + bool is_fun() const { return type == TokenType::Function; } + // жǷΪ "("ڱʽ + bool is_LP() const { return type == TokenType::LeftParen; } + // жǷΪ ")"ڱʽ + bool is_RP() const { return type == TokenType::RightParen; } + // жǷΪ "x", "y"չַ֧Ŵ + bool is_var()const { return type == TokenType::Variable; } + +}; +#endif \ No newline at end of file diff --git a/ScientificCalculator/Tokenizer.cpp b/ScientificCalculator/Tokenizer.cpp new file mode 100644 index 0000000..17d869f --- /dev/null +++ b/ScientificCalculator/Tokenizer.cpp @@ -0,0 +1,115 @@ +#include"Tokenizer.h" +#include +#include + +using namespace std; + +vector Tokenizer::run(const string& expr) { + expression = expr; + pos = 0; + vector tokens; + + // ѭַȡʽ + while (pos < expression.length()) { + skipWhitespace();// հַ + if (pos >= expression.length()) break; + + char current = expression[pos]; + + // ֻһԪſͷȡ + if (is_dig(current) || (current == '-' && is_UMC(tokens))) + tokens.push_back(readNumber()); + + // ĸȡ + else if (is_let(current)) + tokens.push_back(readIdentifier()); + + // ȡ + else + tokens.push_back(readOperatorOrParen()); + } + + return tokens; +} + +void Tokenizer::skipWhitespace() { + // пոƱȿհַ + while (pos < expression.length() && isspace(expression[pos])) + ++pos; +} + +Token Tokenizer::readNumber() { + size_t start = pos; + bool hasDecimal = false;// СǷֹ + + if (expression[pos] == '-') + ++pos; // һԪţ + + while (pos < expression.length() && (is_dig(expression[pos]) || expression[pos] == '.')) { + if (expression[pos] == '.') { + if (hasDecimal) + throw runtime_error("ָʽЧС");// Сظ + hasDecimal = true; + } + ++pos; + } + + string numberStr = expression.substr(start, pos - start); + return Token(TokenType::Number, numberStr);// Token +} + +Token Tokenizer::readIdentifier() { + size_t start = pos; + + // ȡĸַ + while (pos < expression.length() && is_let(expression[pos])) + ++pos; + + string name = expression.substr(start, pos - start); + + //ʶΪΪ + skipWhitespace(); + if (pos < expression.length() && expression[pos] == '(') + return Token(TokenType::Function, name); + else + return Token(TokenType::Variable, name); +} + +Token Tokenizer::readOperatorOrParen() { + char c = expression[pos++]; // ȡǰַָ + + // жַͲӦ Token + switch (c) { + 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, "^", 3, Associativity::Right); + case '(': return Token(TokenType::LeftParen, "("); + case ')': return Token(TokenType::RightParen, ")"); + default: + throw runtime_error(string("δ֪ ") + c); // δַ֪ + } +} + +bool Tokenizer::is_UMC(const vector& tokens) { + // DZʽͷǰһţǰΪһԪ + if (tokens.empty()) return true; + Token last = tokens.back(); + return last.is_ope() || last.is_LP(); +} + +bool Tokenizer::is_dig(char c) const { + // жǷΪַ + return std::isdigit(static_cast(c)); +} + +bool Tokenizer::is_let(char c) const { + // жǷΪӢĸ + return std::isalpha(static_cast(c)); +} + +bool Tokenizer::is_OpeCh(char c) const { + // жǷΪϷַ + return c == '+' || c == '-' || c == '*' || c == '/' || c == '^'; +} diff --git a/ScientificCalculator/Tokenizer.h b/ScientificCalculator/Tokenizer.h new file mode 100644 index 0000000..2fb3052 --- /dev/null +++ b/ScientificCalculator/Tokenizer.h @@ -0,0 +1,40 @@ +#ifndef TOKENIZER_H +#define TOKENIZER_H + +#include +#include +#include"Token.h" + +using namespace std; + +class Tokenizer { +public: + //ʽַתΪToken + vector run(const string& expr); + +private: + string expression; //ǰıʽ + size_t pos = 0; //ǰָλ + + //ߺ:ոַ + void skipWhitespace(); + + //ȡһ֣СС㡢ܵһԪţ + Token readNumber(); + + //ȡ(sincoslog)xy + Token readIdentifier(); + + //ȡ + Token readOperatorOrParen(); + + //жϵǰλǷһԪŵ + bool is_UMC(const vector& tokens); + + //ַжϸ + bool is_dig(char c)const; + bool is_let(char c)const; + bool is_OpeCh(char c)const; +}; + +#endif \ No newline at end of file diff --git a/ScientificCalculator/main.cpp b/ScientificCalculator/main.cpp new file mode 100644 index 0000000..d4c0735 --- /dev/null +++ b/ScientificCalculator/main.cpp @@ -0,0 +1,29 @@ +#include +#include +#include "Calc.h" + +using namespace std; + +int main() { + string input; + cout << "ѧ ( q ˳)\n"; + + while (true) { + cout << "ʽ > "; + getline(cin, input); + + if (input == "q" || input == "quit") + break; + + double result; + if (Calc::safeEval(input, result)) { + cout << "= " << result << endl; + } + else { + cout << "[] ʽ޷\n"; + } + } + + cout << "лʹãټ" << endl; + return 0; +}