3.栈的应用-表达式求值

实验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.14159265359
    • e=2.718281828
    • ans 保存了上一次的运算结果

代码

// #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();
}

运行截图

posted @ 2020-09-19 14:30  zaxtyson  阅读(999)  评论(0编辑  收藏  举报