實驗3-棧的應用-表達式求值
1、實驗目的:
- 掌握棧的定義及實現;
- 掌握利用棧求解算術表達式的方法。
2、實驗內容:
- 通過修改完善教材中 P78-79 的算法,利用棧來實現算術表達式求值的算法。
- 程序運行時,輸入合法的算術表達式(中間值及最終結果要在 0~9 之間,可以包括加減乘除和括號),便可輸出相應的計算結果。
說明
- 發的代碼有幾處變量類型錯誤,使得
char
變為int
,導致運行結果異常,已經修改
參考代碼
#include <iostream>
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;
typedef char SElemType;
struct SNode
{
SElemType data; // 此處類型錯誤,不是int而是char(SElemType)
struct SNode *next;
};
typedef SNode *LinkStack;
// ------------- 鏈棧相關函數 -------------------------
Status InitStack(LinkStack &S)
{
S = NULL;
return OK;
}
bool StackEmpty(LinkStack &S)
{
if (!S) // 棧不存在,返回true
return true;
return false;
}
Status Push(LinkStack &S, SElemType e)
{
SNode *p;
p = new SNode;
p->data = e;
p->next = S;
S = p;
return OK;
}
Status Pop(LinkStack &S, SElemType &e)
{
if (StackEmpty(S))
return ERROR;
e = S->data;
SNode *p = S;
S = S->next;
delete p;
return OK;
}
SElemType GetTop(LinkStack &S) // 這里返回類型錯誤,不是int(Status),而是char(SElemType)
{
if (!StackEmpty(S))
return S->data;
}
// -------------------------- End of Stack ----------------------------------
// ------------------------ 表達式求值相關函數--------------------------
// 判斷ch是否為運算符
const char oper[7] = {'+', '-', '*', '/', '(', ')', '#'};
bool In(char ch)
{
for (int i = 0; i < 7; i++)
{
if (ch == oper[i])
{
return true;
}
}
return false;
}
// 判斷運算符優先級
char Precede(char theta1, char theta2)
{
if ((theta1 == '(' && theta2 == ')') || (theta1 == '#' && theta2 == '#'))
{
return '=';
}
else if (theta1 == '(' || theta1 == '#' || theta2 == '(' || (theta1 == '+' || theta1 == '-') && (theta2 == '*' || theta2 == '/'))
{
return '<';
}
else
return '>';
}
// 計算兩數運算結果
char Operate(char first, char theta, char second)
{
switch (theta)
{
case '+':
return (first - '0') + (second - '0') + 48; //為什么要減 0, 要加 48
case '-':
return (first - '0') - (second - '0') + 48;
case '*':
return (first - '0') * (second - '0') + 48;
case '/':
return (first - '0') / (second - '0') + 48;
}
return 0;
}
char EvaluateExpression()
{
LinkStack OPTR, OPND; //算術表達式求值的算符優先算法,設OPTR和OPND分別為運算符棧和操作數棧
char ch, theta, a, b, x, top;
InitStack(OPND); //初始化OPND棧
InitStack(OPTR); //初始化OPTR棧
Push(OPTR, '#'); //將表達式起始符“#”壓入OPTR棧
cin >> ch;
while (ch != '#' || (GetTop(OPTR) != '#')) //表達式沒有掃描完畢或OPTR的棧頂元素不為“#”
{
if (!In(ch))
{
Push(OPND, ch);
cin >> ch;
} //ch不是運算符則進OPND棧
else
switch (Precede(GetTop(OPTR), ch)) //比較OPTR的棧頂元素和ch的優先級
{
case '<':
Push(OPTR, ch);
cin >> ch; //當前字符ch壓入OPTR棧,讀入下一字符ch
break;
case '>':
Pop(OPTR, theta); //彈出OPTR棧頂的運算符
Pop(OPND, b);
Pop(OPND, a); //彈出OPND棧頂的兩個運算數
Push(OPND, Operate(a, theta, b)); //將運算結果壓入OPND棧
break;
case '=': //OPTR的棧頂元素是“(”且ch是“)”
Pop(OPTR, x);
cin >> ch; //彈出OPTR棧頂的“(”,讀入下一字符ch
break;
} //switch
} //while
return GetTop(OPND); //OPND棧頂元素即為表達式求值結果
}
int menu()
{
int c;
cout << "0-9以內的多項式計算" << endl;
cout << "1.計算" << endl;
cout << "0.退出" << endl;
cout << "選擇:";
cin >> c;
return c;
}
// ------------- 程序入口 --------------------------------
int main()
{
char res;
while (true)
{
switch (menu())
{
case 1:
cout << "請輸入要計算的表達式(操作數和結果都在0-9的范圍內,以#結束):" << endl;
res = EvaluateExpression(); //算法3.22 表達式求值
cout << "計算結果為" << res - 48 << endl
<< endl;
break;
case 0:
cout << "退出成功" << endl;
exit(0);
default:
break;
}
}
}
OOP 版
使用了 C++17 的特性,編譯時加上參數
-std=c++17
在實驗要求的基礎是進行了擴展,添加了一點功能
- 支持浮點數運算
- 支持基本運算加(+)、減(-)、乘(*)、除(/)、取余(%)、乘方(^)運算
- 支持對數運算
ln(N)
logN(M)
- 支持三角函數運算
sin(N)
cos(N)
tan(N)
- 支持反三角函數運算
asin(N)
acos(N)
atan(N)
- 其它運算
- 求絕對值
abs(N)
- 平方根
sqrt(N)
- 向上取整
ceil(N)
向下取整floor(N)
- 產生
[0~1]
的隨機數random()
- 求絕對值
- 內置常量
pi
=3.14159265359e
=2.718281828ans
保存了上一次的運算結果
代碼
// #define _DEBUG // 開啟調試
#include <iostream>
#include <optional> // C++17
#include <string>
#include <sstream>
#include <vector>
#include <map>
#include <cmath>
#include <regex>
#include <random>
using std::cin;
using std::cout;
using std::endl;
using std::map;
using std::optional;
using std::ostringstream;
using std::string;
using std::vector;
// Stack template class
// Examples:
// Stack<int> stack;
// auto top = stack.GetTop();
// cout << top.value_or(-1) << endl; // top == -1 if stack is empty
// stack.Push(100);
// stack.Push(44);
// stack.Pop();
// stack.Push(97);
// cout << stack.Length() << endl; // 2
// if (!stack.IsEmpty()) // check Stack status before GetTop() manually
// cout << stack.GetTop().value() << endl; // 97
template <typename ElemType>
class Stack
{
struct Node
{
ElemType data;
Node *next;
};
private:
size_t length_; // 當前棧高
Node *head_; // 頭結點(無數據)
public:
Stack();
~Stack();
Stack(const Stack &) = delete;
Stack &operator=(const Stack &) = delete;
size_t Length() const { return length_; }
bool IsEmpty() const { return length_ == 0; }
bool Push(const ElemType e);
optional<ElemType> Pop();
optional<ElemType> GetTop();
};
template <typename ElemType>
Stack<ElemType>::Stack() : length_(0)
{
head_ = new Node();
if (!head_)
throw std::bad_alloc(); // 內存分配失敗
head_->next = nullptr;
}
template <typename ElemType>
Stack<ElemType>::~Stack()
{
Node *p = head_->next;
while (p)
{
head_->next = p->next;
// cout << "Deleted: " << p->data << endl;
delete p;
p = head_->next;
}
delete head_;
}
template <typename ElemType>
bool Stack<ElemType>::Push(const ElemType e)
{
Node *node = new Node();
if (!node)
return false;
node->data = e; // 前插法
node->next = head_->next;
head_->next = node;
length_++;
return true;
}
template <typename ElemType>
optional<ElemType> Stack<ElemType>::GetTop()
{
if (head_->next)
return head_->next->data;
return std::nullopt;
}
template <typename ElemType>
optional<ElemType> Stack<ElemType>::Pop()
{
if (IsEmpty())
return std::nullopt;
Node *p = head_->next;
ElemType e = p->data; // 保存棧頂元素
head_->next = p->next;
delete p;
length_--;
return e;
}
// ------------ End of Stack ------------------------
class SimpleCalculator
{
private:
double ans = 0.0;
map<string, int> op_priority = {
{"(", 10}, {"random", 7}, {"sqrt", 7}, {"sin", 7}, {"cos", 7}, {"tan", 7},
{"acos", 7}, {"asin", 7}, {"atan", 7}, {"ln", 7}, {"log", 7}, {"abs", 7},
{"log", 7}, {"floor", 7}, {"ceil", 7}, {"^", 3}, {"%", 2}, {"*", 2},
{"/", 2}, {"+", 1}, {"-", 1}, {")", -1}}; // 運算符優先級
public:
SimpleCalculator() = default;
SimpleCalculator(const SimpleCalculator &) = delete;
SimpleCalculator &operator=(const SimpleCalculator &) = delete;
void Run();
void ShowHelp() const;
vector<string> ToSuffixExpr(string &); // 轉換為后綴表達式
vector<string> PreProcess(string &); // 預處理輸入的表達式字符串, 算法界限符與數值分離
double Evaluate(string &); // 計算后綴表達式的值
};
void SimpleCalculator::ShowHelp() const
{
cout << "SimpleCalculator v0.1\n\n"
<< "Supported Operates: \n"
<< "\t+ - * / ^ %\n"
<< "\tsin(N) cos(N) tan(N)\n"
<< "\tsin(N) acos(N) atan(N)\n"
<< "\tln(N) logN(M) random()\n"
<< "\tabs(N) ceil(N) floor(N) sqrt(N)\n"
<< "\nInner constant:\n"
<< "\tpi=3.14159265359\te=2.718281828"
<< endl;
}
vector<string> SimpleCalculator::PreProcess(string &expr)
{
expr = std::regex_replace(expr, std::regex("\\s+"), ""); // 刪除空格
expr = std::regex_replace(expr, std::regex("\\bpi"), "3.14159265359"); // 替換 pi
expr = std::regex_replace(expr, std::regex("\\be"), "2.718281828"); // 替換 e
expr = std::regex_replace(expr, std::regex("\\bans"), std::to_string(ans)); // 替換上次計算的結果
expr = std::regex_replace(expr, std::regex("^-(\\d)"), "0-$1"); // 負數開頭補0
expr = std::regex_replace(expr, std::regex("(\\D)(-)(\\d)"), "$010$02$03"); // 表達式內部負數前補0
expr = std::regex_replace(expr, std::regex("([\\-\\+\\*/%\\^\\(\\)]|log)"), " $1 "); // 數值和算符用空格分開, 因為logN(M), log 作為整體,
std::istringstream iss(expr);
vector<string> results((std::istream_iterator<string>(iss)), std::istream_iterator<string>()); // 使用空格 split
#ifdef _DEBUG
cout << "DEBUG: Expression split: ";
for (auto s : results)
cout << "[" << s << "] ";
cout << endl;
#endif
return results;
}
vector<string> SimpleCalculator::ToSuffixExpr(string &expr)
{
vector<string> expr_list = PreProcess(expr);
vector<string> ret;
Stack<string> op_stack;
for (string str : expr_list)
{
if (isdigit(str[0]))
ret.push_back(str);
else if (op_stack.IsEmpty() || op_priority[str] > op_priority[op_stack.GetTop().value()])
op_stack.Push(str);
else if (str == ")")
{
while (op_stack.GetTop().value() != "(")
ret.push_back(op_stack.Pop().value());
op_stack.Pop(); // 丟棄棧中匹配的 (
}
else
{
while (!op_stack.IsEmpty() && op_stack.GetTop().value() != "(")
ret.push_back(op_stack.Pop().value());
op_stack.Push(str);
}
}
while (!op_stack.IsEmpty())
ret.push_back(op_stack.Pop().value());
#ifdef _DEBUG
cout << "DEBUG: Suffix Expression: ";
for (auto s : ret)
cout << "[" << s << "] ";
cout << endl;
#endif
return ret;
}
double SimpleCalculator::Evaluate(string &expr)
{
vector<string> suffix_expr = ToSuffixExpr(expr);
Stack<double> stack;
for (string str : suffix_expr)
{
if (isdigit(str[0])) // 字符串第一個是數字
stack.Push(strtod(str.c_str(), nullptr)); // 轉化為數字入棧
else if (str == "abs")
stack.Push(fabs(stack.Pop().value()));
else if (str == "sqrt")
stack.Push(sqrt(stack.Pop().value()));
else if (str == "sin")
stack.Push(sin(stack.Pop().value()));
else if (str == "asin")
stack.Push(asin(stack.Pop().value()));
else if (str == "cos")
stack.Push(cos(stack.Pop().value()));
else if (str == "acos")
stack.Push(acos(stack.Pop().value()));
else if (str == "tan")
stack.Push(tan(stack.Pop().value()));
else if (str == "atan")
stack.Push(atan(stack.Pop().value()));
else if (str == "ln")
stack.Push(log(stack.Pop().value()));
else if (str == "floor")
stack.Push(floor(stack.Pop().value()));
else if (str == "ceil")
stack.Push(ceil(stack.Pop().value()));
else if (str == "random") // 產生一個 [0, 1) 的隨機數
{
std::random_device rd; // 隨機數生成器
std::mt19937 mt(rd());
std::uniform_real_distribution<double> dist(0.0, 1.0);
stack.Push(dist(mt));
}
else // 二元運算符
{
double rhs = stack.Pop().value(); // 先出棧的運算符右邊的數
double lhs = stack.Pop().value();
if (str == "^")
stack.Push(pow(lhs, rhs));
else if (str == "log")
stack.Push(log(rhs) / log(lhs)); // 換底公式求 log_a(b)
else if (str == "%")
stack.Push(fmod(lhs, rhs));
else if (str == "*")
stack.Push(lhs * rhs);
else if (str == "/")
stack.Push(lhs / rhs);
else if (str == "+")
stack.Push(lhs + rhs);
else if (str == "-")
stack.Push(lhs - rhs);
}
}
return stack.Pop().value(); // 棧頂就是表達式的值
}
void SimpleCalculator::Run()
{
while (true)
{
string input;
cout << ">>> ";
getline(cin, input);
if (input == "help")
{
ShowHelp();
continue;
}
try
{
double output = Evaluate(input);
cout << "\nans=" << output << endl
<< endl;
ans = output; // 保存運算結果下次使用
}
catch (const std::exception &e)
{
cout << "表達式有誤!" << endl;
}
}
}
// --------------------- End of Calculator -------------------------------
int main(int argc, char const *argv[])
{
SimpleCalculator calculator;
calculator.Run();
}