在c++中使用堆栈计算后缀

Evaluate postfix using a stack in C++

本文关键字:堆栈 计算 后缀 c++      更新时间:2023-10-16
#include <iostream>
#include <sstream>
#include <stack>
#include <limits>
#include <string>
using namespace std;
int main()
{
    string input;
    cout << "Enter a postfix expression: " << endl;
    getline(cin, input);
    int operand1, operand2, result,number;
    stack<char>operation;
    stringstream temp;
    int i=0;
    while (i < input.length())
    {
        if (isdigit(input[i]))
        {
            operation.push(input[i]);
        }
        else
        {
            operand2 = operation.top();
            temp << operation.top();
            operation.pop();
            operand1 = operation.top();
            temp << operation.top();
            operation.pop();
            switch(operand1,operand2)
            {
                case '+': result=operand1 + operand2;
                break;
                case '-': result=operand1 - operand2;
                break;
                case '*': result=operand1 * operand2;
                break;
                case '/': result=operand1 / operand2;
                break;
            }
            operation.push(result);
        }
        i++;
    }
    cout << "The result is: "<<temp.str()<<endl;
    cin.ignore(numeric_limits<streamsize>::max(), 'n');
    return 0;
}

我已经改变了代码,并设法获得"pop"值,但操作不起作用。

你可能意味着

switch(input[i])

而不是
switch(operation.top())

更新对代码更改的响应


我可以确认你更改了代码,但不是很好。

  1. 代码大部分已经有了所有的缺陷,还有一些。
  2. 你现在把操作数组合成一个stringstream有什么好处?
  3. 现在打开(operand1,operand2)…
    • 都是未初始化的
    • (operand1,operand2)在此上下文中基本上表示(operand2)(序列操作符)
    • 你的分支标签是…运算符(+ -/*)
  4. 你现在打印一个最终的结果,这是输入中所有数字的连接(如果你曾经达到程序的结束而没有崩溃)?

在之前的错误中,仍然需要修复

  1. 堆栈计算器的心智模型。
    • 数字(整数)是操作数(所以9,100,39829是有效的操作数)
    • +-/*为操作符(operators operands进行操作)
    • 堆栈操作数堆栈,不是操作符堆栈(操作符不需要记住,因为它们立即被求值)
    • 号码由一行1个或多个digits(0123456789)组成;因此,在operand stack
    • 上'push' number之前,您需要阅读几个字符。
    • operators +-/*取2 operands,所以对大小为<2的堆栈的任何操作都是错误的(您需要检查,否则程序将在试图访问不存在或包含垃圾的内存时崩溃)。

这应该足够让你开始了。

认为有两件事是积极的:

  1. 程序编译。+1对于你实际使用编译器:)
  2. 您将重复的operation.push(result)从开关中取出,因此它不再重复。编码风格+1…

我希望你能从这里收集到代码不是很好(委婉地说),我真的认为一些基本的练习是有序的:1. 编写一个简单的for循环,将数字1到10打印到控制台1. 编写一个简单的while循环,打印用户输入的单词1. 使用一个简单的循环打印1到50之间所有是7的倍数的数字1. 当用户输入字母a、b、k或z时,使用switch语句打印"yes"2. 创建一个简单的循环,只打印相同字符后面的每个字符的输入字符(因此'abccdefgghijkllmabcdd'将变成' cld ')1. 使用相同的循环,但这次打印紧跟在相同单词后面的每个单词(因此"no, no, you should not pop, pop, but push, pop"变成了"no pop")

这应该会给你一种感觉是如何真正工作的,没有猜测或"神奇因素"。

哦,别忘了,我在下面为你实现了整个东西。我不建议你盲目地抄(这对你的老师来说是相当明显的:)),但如果你想知道我上面所有的话是什么意思,你可以看一看:)


  1. 您正在推送松散的数字,而不是解析过的数字

  2. 在第31行你弹出一个可能空的堆栈(导致段错误,除非你在编译器上使用调试模式STL标志)

只是为了好玩:

#include <iostream>
#include <stack>
#include <vector>
#include <limits>
#include <string>
#include <stdexcept>
#include <iterator>
#include <fstream>
using namespace std;
    template <class T>
        static void dumpstack(std::stack<T> s/*byval!*/)
    {
        std::vector<T> vec;
        while (!s.empty())
        {
            vec.push_back(s.top());
            s.pop();
        }
        std::copy(vec.rbegin(), vec.rend(), std::ostream_iterator<int>(std::cout, " "));
    }
    class calc
    {
        private:
            std::stack<int> _stack;
            int _accum;
            bool _pending;
            void store(/*store accumulator if pending*/)
            {
                if (_pending)
                {
                    _stack.push(_accum);
                    _pending = false;
                    _accum = 0;
                }
            }
        public:
            calc() : _accum(0), _pending(false) 
            {
            }
            void handle(char ch)
            {
                switch (ch)
                {
                    case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
                        _pending = true;
                        _accum *= 10;
                        _accum += ch-'0';
                        break;
                    case '+': case '-': case '/': case '*':
                        {
                            store();
                            if (_stack.size()<2)
                                throw std::runtime_error("stack underflow");
                            int op2 = _stack.top(); _stack.pop();
                            int op1 = _stack.top(); _stack.pop();
                            switch (ch)
                            {
                                case '+': _stack.push(op1 + op2); break;
                                case '-': _stack.push(op1 - op2); break;
                                case '/': _stack.push(op1 / op2); break;
                                case '*': _stack.push(op1 * op2); break;
                            }
                            // feedback to console:
                            std::cout << std::endl << "(evaluated: " << op1 << " " << ch << " " << op2 << " == " << _stack.top() << ")" << std::endl;
                            dump();
                        }
                        break;
                    default:
                        store(); // todo: notify of ignored characters in input?
                }
            }
            void dump() const
            {
                dumpstack(_stack);
            }
    };
    int main() 
    {
        cout << "Enter postfix expressions: " << endl;
        calc instance;
        try
        {
            while (std::cin.good())
            {
                char ch = std::cin.get();
                instance.handle(ch);
            }
            std::cout << "Final result: "; 
            instance.dump();
            return 0;
        } catch(const std::exception& e)
        {
            std::cerr << "E: " << e.what() << std::endl;
            return 255;
        }
    }

测试输出:(注意,按回车键后,可以继续执行剩余的部分求值的堆栈)

Enter postfix expressions: 
1 2 3 +4 * - / 1333 *
(evaluated: 2 + 3 == 5)
1 5 
(evaluated: 5 * 4 == 20)
1 20 
(evaluated: 1 - 20 == -19)
-19 E: stack underflow

代码有很多错误,首先是对输入表达式的解析。实际的崩溃很可能是由于这样一个事实,如果你输入像"12+"这样的东西,你会把'1''2'推入堆栈(注意:字符1和2,而不是值1和2!!),然后试图提取两个操作数一个你从未插入到堆栈中的操作符。

在解析输入时,您逐个字符读取,并且仅使用第一个数字,解析无法处理空格或任何其他分隔符…试着把问题分成两部分:解析和处理。解析问题可以通过不使用实际读取的值,而只是打印它们(或以某种形式存储,然后打印整个读取表达式)来解决,这可以作为第一步。确保解析器能够以健壮的方式处理常见表达式,如"1 2+"、"10 20 +"、"1 2+"、"1 2+"(注意空格的不同位置)。而且它无法优雅地解析"+"、"1 +"、"1 ++"等表达式……你永远不能相信用户的输入,他们会犯错误,但这不应该让你的程序崩溃。

一旦确定能够解析输入,就开始实际的算法。使其对以前可能无法处理的无效用户输入(如"10 0/")具有健壮性,并进行实际处理。

学会使用调试器,它将帮助你了解当事情出错的原因是什么。调试器会在不到一秒钟的时间内指出上面代码中的特定问题,它不会告诉你为什么它会失败,但它会告诉你它是如何失败的,以及程序的状态。如果我的预感是正确的,那么它将指向operation.top()指令作为罪魁祸首,并且您将能够看到您试图提取的元素多于插入的元素。一步一步地执行程序的一部分来理解它实际在做什么,你会注意到,当你读到"12+"时,你实际上是将两个看似无关的整数存储到堆栈中('1''2'的ASCII值…