九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

堆栈的应用(2) 中缀算术表达式到后缀(逆波兰记法reverse polish notation)的转换及其计算 C++实现

堆栈的应用(2)

中缀算术表达式到后缀(逆波兰记法reverse polish notation)的转换及其计算 C++实现

write by** 九天雁翎(JTianLing) –
blog.csdn.net/vagrxie
**

«Data Structures and Algorithm Analysis in C++»

–《数据结构与算法分析c++描述》 Mark Allen Weiss著 人民邮电大学出版 中文版第73-77面,中缀算术表达式到后缀(逆波兰记法reverse polish notation)的转换及其计算

   目前仅仅实现文中说明的功能,目标并不是一个完整的四则运算程序,所以只支持加法,乘法和()。另外,因为对于C++流的控制能力比较弱(我下一步就决定好好研究研究),所以对输入的格式要求非常严格。

必须是1 + 2 * 3 =

的格式,每个数字和符号之间都需要空格,一个比较复杂的例子是:

text 1 + 2 * 3 + ( 1 + 2 * 3 ) =

转换后:

``text 1 2 3 *

  • 1 2 3 * + + = ``

先看测试程序,就应该能知道,大概实现了什么效果了,这里唯一的便利在于,用了C++的输入输出流后,对于iostream,stringstream都比较一致了,但是还是感觉自己对流的控制能力太弱了。

test.cpp:

#include <stdio.h>
#include <stdlib.h>
#include <stack>
#include <string>
#include <iostream>
#include <sstream>
#include "ExprComputer.h"

using namespace std;

int main(int argc, char * argv[])
{
    CExprComputer loExprComputer;
    // all these below can work right,comment is same as the content 'couted'.
    stringstream lss;
    lss << "1 + 2 * 3 =";

    cout <<"test trans stringstream in and cout." <<endl;
    loExprComputer.TransInfix2Postfix(lss, cout);

    cout <<"test trans cin and cout." <<endl;
    loExprComputer.TransInfix2Postfix(cin, cout);

    stringstream lss2;
    cout <<"test trans cin and stringstream out." <<endl;
    loExprComputer.TransInfix2Postfix(cin, lss2);
    cout << lss2.str() <<endl;

    lss.seekg(0);
    cout <<"test stringstream in computeInfix." <<endl;
    cout << lss.str();
    cout << loExprComputer.ComputeInfix(lss) <<endl;

    cout <<"test cin in computeInfix." <<endl;
    cout << loExprComputer.ComputeInfix(cin) <<endl;

    stringstream lssPostfix;
    lssPostfix << "1 2 3 * + =";
    cout <<"test stringstream in ComputePostfix." <<endl;
    cout << lssPostfix.str();
    cout << loExprComputer.ComputePostfix(lssPostfix) <<endl;

    cout <<"test cin in ComputePostfix." <<endl;
    cout << loExprComputer.ComputePostfix(cin) <<endl;

    cout <<"Test completed." <<endl;
    exit(0);
}

ExprComputer头文件:

#ifndef __EXPR_COMPUTE_H__
#define __EXPR_COMPUTE_H__

#include <iostream>
#include <sstream>
#include <stack>
using namespace std;

// argument to input
#define _IN_ 

// argument to output
#define _OUT_ 

class CExprComputer
{
public :
    int ComputeInfix(_IN_ istream& aisExpr);
    int ComputePostfix(_IN_ istream& aisExpr);

    // Transform a infix expression to Postfix expression
    int TransInfix2Postfix(_IN_ istream& aisInfix,
            _OUT_ ostream& aosPostfix);

private :
    // Stack should be empty,Dump the information still in Stack and exit
    void DumpStack();

    // Output all information still in Stack
    void OutputStack();

    // Make sure Stack is not empty when it should not.
    void CheckStack();

    // I don't know why Stack operator is so few.And why I need to
    // clear the Stack in this example? GOD knows.
    void ClearStack();

    // Read a int or a operator from a stream
    bool ReadStream(_IN_ istream& aisExpr, _OUT_ int & aiReaded,  _OUT_ bool & abIsChar);

    // Process a Operator
    void ProcessOperator(char ac, _OUT_ ostream& aosPostfix);

    void ComputeOperator(char ac);

    stack<int> miSta;
};

#endif

cpp 文件:

#include "ExprComputer.h"

void CExprComputer::DumpStack()
{
    if(!miSta.empty())
    {
        cout <<"stack: ";
        OutputStack();
        cout <<endl;
        exit(1);
    }

}

void CExprComputer::OutputStack()
{
    while(!miSta.empty())
    {
        cout <<miSta.top() <<" ";
        miSta.pop();
    }
}

void CExprComputer::ClearStack()
{
    while(!miSta.empty())
    {
        miSta.pop();
    }
}

void CExprComputer::CheckStack()
{
    if(    miSta.empty() )
    {
        cout <<"Invalid expression input." <<endl;
        exit(1);
    }
}

bool CExprComputer::ReadStream(_IN_ istream& aisExpr, _OUT_ int & aiReaded,  _OUT_ bool & abIsChar)
{
    if(aisExpr.eof())
    {
        return false;
    }

    // try to read stream as a int
    abIsChar = false;
    int li;
    aisExpr >> li;

    // if next thing in stream is not a int, back a char and read it
    if(aisExpr.fail())
    {
        aisExpr.clear();
        aisExpr.unget();
        char c;
        aisExpr >> c;
        if(c != '=')
        {
            aiReaded = c;
            abIsChar = true;
            return true;
        }
        else
        {
            return false;
        }
    }

    aiReaded = li;
    return true;
}

void CExprComputer::ProcessOperator(char ac, _OUT_ ostream& aosPostfix)
{
    switch(ac)
    {
        case '(':
        {
            // save the '('
            miSta.push(ac);
            break ;
        }
        case ')':
        {
            char lc;
            CheckStack();

            // output all operator until find a '('
            while( (lc = miSta.top()) != '(' )
            {
                aosPostfix << lc <<" ";
                miSta.pop();
                CheckStack();
            }

            // the first '(' in stack,We just need pop it
            miSta.pop();
            break ;
        }
        case '*':
        {
            char lc;

            // output all operator until find a lower level operator
            while( !miSta.empty() &&
                    ((lc = miSta.top()) != '(') &&
                    ( lc != '+') )
            {
                aosPostfix <<lc <<" ";
                miSta.pop();
            }

            miSta.push(ac);
            break ;
        }
        case '+':
        {

            char lc;

            // output all operator until find a lower level operator
            while( !miSta.empty() &&
                    ((lc = miSta.top()) != '(') )
            {
                aosPostfix <<lc <<" ";
                miSta.pop();
            }

            miSta.push(ac);
            break ;
        }
        default :
        {
            cout <<"Don't support this operator." <<endl;
            exit(1);
        }
    }

}

int CExprComputer::TransInfix2Postfix(_IN_ istream& aisInfix,
        _OUT_ ostream& aosPostfix)
{
    int li = 0;
    bool lbIsChar = false;
    while( ReadStream(aisInfix, li, lbIsChar) )
    {
        // number need immediately output
        if(!lbIsChar)
        {
            aosPostfix <<li <<" ";
        }
        else
        {
            char lc = static_cast<char>(li);
            ProcessOperator(lc, aosPostfix);
        }
    }

    while(!miSta.empty())
    {
        aosPostfix <<(char)miSta.top() <<" ";
        miSta.pop();
    }
    aosPostfix <<'=' <<endl;

    return 1;
}

int CExprComputer::ComputePostfix(_IN_ istream& aisExpr)
{
    ClearStack();

    int li = 0;
    bool lbIsChar = false;
    while( ReadStream(aisExpr, li, lbIsChar) )
    {
        // number need immediately output
        if(!lbIsChar)
        {
            miSta.push(li);
        }
        else
        {
            char lc = static_cast<char>(li);
            ComputeOperator(lc);
        }
    }

    CheckStack();
    int liResult = miSta.top();
    miSta.pop();

    DumpStack();

    return liResult;
}

void CExprComputer::ComputeOperator(char ac)
{
    // get lhs and rhs
    CheckStack();
    int lilhs = miSta.top();
    miSta.pop();
    CheckStack();
    int lirhs = miSta.top();
    miSta.pop();

    switch(ac)
    {
        case '*':
        {
            int liResult = lilhs * lirhs;
            miSta.push(liResult);
            break ;
        }
        case '+':
        {
            int liResult = lilhs + lirhs;
            miSta.push(liResult);
            break ;
        }
        default :
        {
            cout <<"Don't support this operator." <<endl;
            exit(1);
        }
    }
}

int CExprComputer::ComputeInfix(_IN_ istream& aisExpr)
{
    stringstream lss;
    TransInfix2Postfix(aisExpr, lss);
    return ComputePostfix(lss);
}

write by** 九天雁翎** (JTianLing) –
blog.csdn.net/vagrxie

分类:  算法 
标签:  C++  reverse polish notation  堆栈  逆波兰记法 

Posted By 九天雁翎 at 九天雁翎的博客 on 2008年12月21日

前一篇: 堆栈的简单C++实现 后一篇: 队列(queue)的链表(list)实现及循环数组(circular array)实现 C++实现