我的算法中用于评估基本括号表达式的缺陷在哪里?

时间:2015-10-20 05:58:52

标签: c# algorithm parsing recursion

我正在尝试实现类似

的程序

输入:

~1^(2~&3)|1 69 11111 -12 

输出:

[whatever NOT 69 XOR (11111 NAND -12) OR 69 equals]

所有按位运算符具有相同的优先级,从左到右计算。我正在构建的算法基本上是

Get number n1 (or evaluation of expression in parenthesis)
Set result = n1
Get operator o1
Get number n2 (or evaluation of expression in parenthesis)
Set result = result op n2
Get operator o2
Get number n3 (or evaluation of expression in paranthesis)
Set result = result o2 n3
Etcetera

我还没完成,但我至少应该能够评估

((1)) 69

会导致

69

我已经测试了

(1) 69

结果

69

这意味着我只需要弄清楚嵌套括号出了什么问题。

我的代码的相关部分,很好地评论,是......在这里忍受我......

    private static long? EvalInner ( string eqtn, Tuple<int,int> eqtnBnds, Dictionary<int,long> eqtnArgs)
    {
        //     eqtn: Equation to be parsed 
        // eqtnBnds: Bounds that define sub-equation being evaluated.
        // eqtnargs: Equation arguments

        // Parses and returns the evaluation of the equation eqtn in the bounds [eqtnBands.Item1, eqtnBnds.Item2)

        // Handle case of empty sub-equation:
        if (eqtnBnds.Item1 == eqtnBnds.Item2) throw new Exception(String.Format("Encountered empty equation at index {0}", eqtnBnds.Item1));


        long? result = null; 
        char? lastop = null; // last operator found 
        bool negateNextNum = false;
        PARSEMODE CURMODE = PARSEMODE.NUM; // beginning of equation should be a number since the form of the equation is
                                           // <NUM><OPER><NUM><OPER>...<OPER><NUM>
        int curidx = eqtnBnds.Item1, offend = eqtnBnds.Item2;
        while ( curidx < offend )
        {
            switch ( CURMODE )
            {
                case PARSEMODE.NUM: // Expecting character at current index to be the beginning of a 
                {
                    if ( eqtn[curidx] >= '0' && eqtn[curidx] <= '9' ) // beginning of the int corresponding to the argument index
                    {
                        int begidx = curidx;
                        // Increment curidx to one after the last digit or the end of the subequation
                        while ( ++curidx < offend && eqtn[curidx] >= '0' && eqtn[curidx] <= '9' );
                        // Try to get an integer representation of the argument. If an error is encountered in that parsing,
                        // throw an error. If the argument is one within the range of arguments given in the command line, 
                        // get its value and update result accordingly. If not, throw an error.
                        int argnum; 
                        if ( Int32.TryParse(eqtn.Substring(begidx, curidx - begidx), out argnum) )
                        {
                            if (eqtnArgs.ContainsKey(argnum))
                            {
                                result = (result != null ? BinOpEval(result, lastop, eqtnArgs[argnum]) : eqtnArgs[argnum]);
                                if (negateNextNum)
                                {
                                    result = ~result;
                                    negateNextNum = false;
                                }
                            }
                            else
                                throw new Exception(String.Format("Argument {0} found in the equation beginning at index {1} is out-of-bounds",
                                                                   argnum,
                                                                   begidx)
                                                    );
                        }
                        else
                        {
                            throw new Exception(String.Format("Trouble parsing argument number that begins at index {0}", 
                                                               begidx)
                                                );
                        }
                        CURMODE = PARSEMODE.OPER;
                    }
                    else if ( eqtn[curidx] == Calculator.OPENPAREN ) // beginning of subequation in paranthesis
                    {
                        int begidx = curidx, netparens = 1;
                        while ( ++curidx < offend && netparens != 0 )
                        {
                            if      ( eqtn[curidx] == Calculator.OPENPAREN  )  ++netparens;
                            else if ( eqtn[curidx] == Calculator.CLOSEPAREN )  --netparens;
                        }
                        if ( netparens != 0 ) // didn't find closing parenthesis
                            throw new Exception(String.Format("Couldn't find closing paranthesis for opening paranthesis at index {0}",
                                                               begidx)
                                                );
                        long? presult = null; // to be the result of the evaluated subequation between the set of parenthesis
                        try { presult = EvalInner(eqtn,new Tuple<int, int>(++begidx, curidx - begidx),eqtnArgs); }
                        catch ( Exception e ) { throw e; }
                        result = (result != null ? BinOpEval(result,lastop,presult) : presult);
                        if (negateNextNum)
                        {
                            result = ~result;
                            negateNextNum = false;
                        }
                        // Upon leaving this else-if block, curidx should be 1 after the closing paranthesis
                        // Expect operate to following closing paranthesis
                        CURMODE = PARSEMODE.OPER; // expecting operator after parens
                    }
                    else if ( eqtn[curidx] == Calculator.NOT ) // NOT symbol preceding number
                    {
                        negateNextNum = !negateNextNum;
                        ++curidx;
                        // CURMODE stays as num
                    }
                    else // unexpected character where beginning of number expected
                    {
                        throw new Exception(String.Format("Expected beginning of number at index {0}, instead got {1}",
                                                            curidx,
                                                            eqtn[curidx])
                                            );
                    }
                    break;
                }

                case PARSEMODE.OPER:
                {
                     // ... 

我正试图弄清楚

的逻辑原因
((1)) 69

正在打印错误

Error: Encountered empty equation at index 2

让我讲述我的逻辑:

首先是curidx = 0eqtn[curidx] = '('CURMODE = PARSEMODE.NUM。这导致我们进入区块

                    else if ( eqtn[curidx] == Calculator.OPENPAREN ) // beginning of subequation in paranthesis
                    {
                          // ... 
                    }

在块的部分结尾

                        int begidx = curidx, netparens = 1;
                        while ( ++curidx < offend && netparens != 0 )
                        {
                            if      ( eqtn[curidx] == Calculator.OPENPAREN  )  ++netparens;
                            else if ( eqtn[curidx] == Calculator.CLOSEPAREN )  --netparens;
                        }

之后begidx0curidx5(右括号后的索引1)。因此,++begidx1curidx - begidx(在++begidx后评估)为4。因此,在致电后

EvalInner(eqtn,new Tuple<int, int>(++begidx, curidx - begidx),eqtnArgs); }

我们最终回到了街区

                    else if ( eqtn[curidx] == Calculator.OPENPAREN ) // beginning of subequation in paranthesis
                    {
                        int begidx = curidx, netparens = 1;
                        while ( ++curidx < offend && netparens != 0 )
                        {
                            if      ( eqtn[curidx] == Calculator.OPENPAREN  )  ++netparens;
                            else if ( eqtn[curidx] == Calculator.CLOSEPAREN )  --netparens;
                        }

并在上面的begidx = 1curidx = 4之后(右括号后的索引1)。然后++begidx = 2以及此后curidx - begidx = 2,当我们重新输入EvalInner时,我们有n[curidx] = '1',导致我们进入该区块

                    if ( eqtn[curidx] >= '0' && eqtn[curidx] <= '9' ) // beginning of the int corresponding to the argument index
                    {
                       // ... 
                    }

之后,显然,我们已经将1与参数69相关联,并通过调用堆栈传递回输出。

我哪里出错?

2 个答案:

答案 0 :(得分:2)

至于你的代码确实显示了相关的工作并且修复它可能不是直截了当的,我更愿意编写一个递归算法来提供你想要的功能。

以下代码的基本思路:

  • removeAllParen启动解析过程。它表现基本 有效性检查(即,开括号和右括号的数量相同) 判断是否需要对执行实际分析的功能进行新调用。所有这些调用都是以递归方式构建最终输出。
  • removeOneParen只处理一组括号,最外部的括号(例如,来自“(((1)))”输出为“((1))”。它 寻找第一个左括号和最后一个左括号。

请记住,这只是一个示例代码,可帮助您了解如何递归地解决此问题。这不是一个全面的括号解析方法,而是适合您特定条件的方法。例如,第二个函数在“(1)(1)”的情况下会失败(虽然调整它并不太困难;你必须依赖类似于你在做的开/关 - 括号计数方法你的代码)。

private string removeAllParen(string input)
{
    string output = input;

    int openingCount = 0;
    int closingCount = 0;
    do
    {
        openingCount = output.Split('(').Length - 1;
        closingCount = output.Split(')').Length - 1;
        if (openingCount != closingCount || openingCount < 1 || closingCount < 1)
        {
            if (openingCount != closingCount) output = "ERROR";
            break;
        }
        output = removeOneParen(output);
    } while (true);


    return output;
}

private string removeOneParen(string input)
{
    string output = input;
    int count = 0;

    bool error = false;
    int iIni = 0;
    int iEnd = output.Length - 1;
    while (count < 2)
    {
        count = count + 1;
        bool ended = false;
        if (count == 2)
        {
            iIni = output.Length - 1;
            iEnd = 0;
        }
        int i = iIni;
        while (!ended)
        {
            string curBit = output.Substring(i, 1);
            if (curBit == "(" || curBit == ")")
            {
                if (count == 1 && curBit == "(" && i < output.Length - 2) output = output.Substring(i + 1);
                else if (count == 2 && curBit == ")") output = output.Substring(0, i);
                else error = true;
                break;
            }
        }
        if (error) break;
    }

    if (error) output = "ERROR";

    return output;
}

更新

为了完整起见,在这里你有另一个版本removeOneParen根据上述开/关括号计数提供更全面的答案。

private string removeOneParen(string input)
{
    string output = "ERROR";

    int openingCount = 0;
    int closingCount = 0;
    bool firstOpening = true;
    int startI = -1;
    for (int i = 0; i < input.Length; i++)
    {
        string curBit = input.Substring(i, 1);
        if (curBit == "(")
        {
            openingCount = openingCount + 1;
            if (firstOpening)
            {
                startI = i + 1;
                firstOpening = false;
            }
        }
        else if (curBit == ")")
        {
            closingCount = closingCount + 1;
            if (openingCount == closingCount)
            {
                if(startI > 0) output = input.Substring(0, startI - 1);
                output = output + input.Substring(startI, i - startI);
                if(i < input.Length - 1) output = output + input.Substring(i + 1);
                break;
            }
        }
    }

    return output;
}

答案 1 :(得分:1)

这里元组的第二个元素是否是子方程的长度或它的上限?

presult = EvalInner(eqtn,new Tuple<int, int>(++begidx, curidx - begidx),eqtnArgs);

简单地让两个命名明确的参数可能更有帮助,但是基于你在递归调用之前的用法,它看起来好像是上限,但是你传入的是长度。

也许这是唯一的问题?不确定。