0

我正在尝试使用堆栈编写后缀表达式转换器的中缀。基本上,它是wikipedia 上的Shutting Yard 算法的一种实现。

/*
    This function returns the precedence of a presented token. To keep comparisons
    simple, the higher the return value, the higher the precedence. Not to be 
    confused with priority.

    From https://web.archive.org/web/20120822075917/http://en.literateprograms.org:80/Special:DownloadCode/Shunting_yard_algorithm_(Python)
*/
int precedence(string token)
{
    switch(token.at(0))
    {
        case '+':
            return 1;
        case '-':
            return 1;
        case '*':
            return 2;
        case '/':
          return 2;
        case '(':
            return 0;
        case ')':
            return 0;
    }

    return 0;
}

/*
  Returns true if the supplied token is an operator. 
*/
bool is_operator(const string& token)
{
    return token == "*" 
        || token == "/" 
        || token == "+" 
        || token == "-";
}

/*
  Returns true if the supplied token is an operand (not an operator). 
*/
bool is_operand(const string& token)
{
    return !is_operator(token) && (token != "(") && (token != ")");
}

void display(const vector<string>& v)
{
    for(unsigned int i=0; i<v.size(); i++)
    {
        cout << v[i] << " ";
    }
    cout << endl;
}

string associativity(const string& token)
{
    if(token == "*" || token == "+")
    {
        return "left";
    }
    else if(token == "/" || token =="-")
    {
        return "left";
    }

    return "?";
}

/*
    From wikipedia:

while there are tokens to be read:
    read a token.
    if the token is a number, then push it to the output queue.
    if the token is an operator, then:
        while (there is an operator at the top of the operator stack with
            greater precedence) or (the operator at the top of the operator stack has
                        equal precedence and
                        the operator is left associative) and
                      (the operator at the top of the stack is not a left bracket):
                pop operators from the operator stack, onto the output queue.
        push the read operator onto the operator stack.
    if the token is a left bracket (i.e. "("), then:
        push it onto the operator stack.
    if the token is a right bracket (i.e. ")"), then:
        while the operator at the top of the operator stack is not a left bracket:
            pop operators from the operator stack onto the output queue.
        pop the left bracket from the stack.
if there are no more tokens to read:
    while there are still operator tokens on the stack:
        pop the operator onto the output queue.
exit.
*/
vector<string> infix_to_postfix(const vector<string>& infix, bool& error)
{
    vector<string> postfix;
    stack<string> operators;

    for(string token : infix)
    {
        if(is_operand(token))
        {
            postfix.push_back(token);
        }
        if(is_operator(token))
        {
            while
            (
                (!operators.empty() && precedence(operators.peek()) > precedence(token))
            || (!operators.empty() && (precedence(operators.peek()) == precedence(token))   && (associativity(token) == "left") && (token != "("))
            )
            {
                string tk = operators.pop();
                if(tk != "(" && tk != ")")
                {
                    postfix.push_back(tk);
                }
            }
            operators.push(token);
        }
        if(token == "(")
        {
            operators.push(token);
        }
        if(token == ")")
        {
            while(!operators.empty() && operators.peek() != "(")
            {
                postfix.push_back(operators.pop());
            }
            if(!operators.empty())
            {
                operators.pop();
            }
        }
    }

    while(!operators.empty())
    {
        postfix.push_back(operators.pop());
    }

    return postfix; 
}

我希望这段代码以包含相关标记的向量的形式返回一个有效的后缀表达式。

下面的评估函数为我正在测试的更长的表达式返回奇怪的数字。

double evaluate(const vector<string>& postfix, bool& error)
{
    error = false;
    double result;
    stack<double> numbers;
    for(unsigned int i=0; i<postfix.size(); i++)
    {
        string token = postfix[i];

        if(is_operand(token))
        {
            numbers.push(stod(token));
        }
        else
        {
            double operand1 = numbers.pop();
            double operand2 = numbers.pop();
            switch(token.at(0))
            {
                case '+':
                    numbers.push(operand1 + operand2);
                    break;
                case '-':
                    numbers.push(operand1 - operand2);
                    break;
                case '*':
                    numbers.push(operand1 * operand2);
                    break;
                case '/':
                    numbers.push(operand1 / operand2);
                    break;
            }
        }
    }

例如,考虑这个输入和输出:

Infix: ( 3 + 3 * 5 ) * 6 - 2 / 1 + 3 + 3 * 5 * ( 4 + 1 )
Postfix: 3 3 5 * + 6 * 2 1 / 3 3 5 4 1 + * * + + -
Result: -29.5

谷歌说它是 184。

更新: 我包含了来自wikipedia的关联函数。我还更新了表达式结果。

更新 2: 合并了使此代码工作的注释。

4

1 回答 1

2

显然你的后缀转换输出已经错了。( 3 + 3 * 5 )应该3 3 5 * +变成3 3 + 5 *

       while
        (
            (!operators.empty() && precedence(token) <= precedence(operators.peek()))
            || (!operators.empty() && operators.peek() != "(")
        )

这部分是错误的。文中说(pred(operators.peek()) > pred(token)) || (pred(operators.peek()) == pred(token) && token != "(")

结果,当运算符不是右括号时,您总是错误地弹出运算符,而忽略运算符的优先级。

        double operand1 = numbers.pop();
        double operand2 = numbers.pop();

那部分也是错误的。操作数 2 是堆栈中较高的操作数。切换它们。

于 2017-12-06T06:10:44.963 回答