【问题标题】:What am I doing wrong? Implementing infix to postfix using a stack我究竟做错了什么?使用堆栈实现中缀到后缀
【发布时间】:2017-12-06 05:51:20
【问题描述】:

我正在尝试使用堆栈编写中缀到后缀表达式转换器。基本上,它是在维基百科上找到的Shunting Yard algorithm 的实现。

/*
    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

Google 说它是 184。

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

更新 2: 合并了使该代码工作的 cmets。

【问题讨论】:

  • 至少,您完全忽略了关联性。
  • 括号是左关联还是右关联?
  • 我认为您正在检查错误运算符的关联性。应该在堆栈顶部检查它,而不是在令牌上检查。更新后的后缀看起来几乎是正确的,只是 - 运算符在错误的位置。
  • 我做了更改,同样的输出。
  • @JoshuaMoore 您的优先谓词现在是错误的。 +- 具有相同的优先级。 */ 具有相同的优先级。您的关联性也是错误的。所有+-*/ 都是左关联的。大括号在 C/C++ 中是右结合的,但这对于这个例子并不重要。你的evaluate 函数也是错误的,它正在切换操作数 1 和 2,导致 -/ 的结果错误。

标签: c++11 shunting-yard


【解决方案1】:

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

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

这部分是错误的。文字上写着(pred(operators.peek()) &gt; pred(token)) || (pred(operators.peek()) == pred(token) &amp;&amp; token != "(")

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

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

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

【讨论】:

  • 我将逻辑更新为(!operators.empty() &amp;&amp; precedence(operators.peek()) &gt; precedence(token)) || (!operators.empty() &amp;&amp; (precedence(operators.peek()) == precedence(token)) &amp;&amp; (token != "(")),现在相同表达式的结果是-43.5
  • 新的“后缀”表达式为:3 3 5 * + 6 * 2 1 / - 3 + 3 5 * 4 * + 1 +
  • 现在显示为:(!operators.empty() &amp;&amp; precedence(operators.peek()) &gt; precedence(token)) || (!operators.empty() &amp;&amp; (precedence(operators.peek()) == precedence(token)) &amp;&amp; (associativity(token) == "left") &amp;&amp; (token != "(")),结果仍然是 -43.5。
  • 我切换了操作数从堆栈中弹出的顺序,新的后缀表达式为:3 3 5 * + 6 * 2 1 / 3 3 5 4 1 + * * + + -。结果现在是 29。
  • @JoshuaMoore 现在撤消对associativity 的实验(您实现的所有运算符都是左关联的)和precedence(加/减必须具有相同的优先级,乘法/除法必须具有相同的优先级)...
猜你喜欢
  • 2013-08-06
  • 1970-01-01
  • 2013-02-26
  • 2016-07-18
  • 2022-06-10
  • 2015-05-22
  • 2019-12-23
  • 1970-01-01
  • 2014-06-15
相关资源
最近更新 更多