2

我正在用 C# 实现调车场算法。尽管它可以很好地解析带有符号(+、* - / 和 ^)的数学表达式。但由于某种原因,它不适用于正弦余弦函数。例如,如果我尝试计算 sin(45) 我得到 0.707106 。但是当我尝试解析表达式时


   sin(25)+cos(15+sin(25))+3  it gives me 0.43756   (Correct answer is: 2.19106879911)

    sin(45)+cos(45) it gives me 0.715779      (Correct answer is: 1.414)

我已按照Wikipedia上的这篇文章中提到的所有步骤进行操作。我已经尝试了几天,但我无法让它完美地工作。这是主要的解析功能

    private void parse()
{
    //scan the input string
    for (int j = 0; j < input.Length; j++)
    {

        if (Char.IsDigit(input[j])) //if its a number
        {
            string number = extractNumber(input, j);  //extracts multiple digit number
            j = currentposition; //increment the counter according to length of the digit
            postfix += number + " ";
            Console.WriteLine(postfix);
        }
            //if its a function character
        else if(Char.IsLetter(input[j]) )
        {
            //its a letter
            string function = getFunction(j); //get the function name
            operators.Push( function );
            j = currentposition;

        }
        else if(input[j] == ',') //if its a comma
        {
            while(operators.Peek() != "(")
            {
                postfix += input[j] + " ";

            }

        }
        else if (IsAnOperator(input[j])) // if we have got an operator
        {
           if (operators.Count != 0 && IsHigherPrecedence(operators.Peek().ToCharArray()[0], input[j]))
            {
                while ( ( operators.Count != 0 && IsHigherPrecedence(operators.Peek().ToCharArray()[0], input[j]) )  )
                { 
                    postfix += operators.Pop() + " "; 

                }
            }

              operators.Push(Char.ToString(input[j]));
        }
        else if (input[j] == '(')
            operators.Push(Char.ToString(input[j]));
        else if (input[j] == ')')
        {
            while (operators.Count != 0 && operators.Peek() != "(")
                postfix += operators.Pop() + " ";
            operators.Pop();
        }


    } // end for loop

        while(operators.Count > 0 )
            postfix +=operators.Pop() + " ";

     //Conversion Logic  (postfix to final answer )

        postfixtokens.AddRange( postfix.Split(' ') ) ;

    for (int j = 0; j < postfixtokens.Count-1; j++)
    {

        if (IsAnOperator(postfixtokens[j][0]) && basket.Count > 1)
        {

            Double second = Double.Parse( basket.Pop() );

            Double first = Double.Parse(basket.Pop() );
            char op = postfixtokens[j][0];
            Double result = ApplyOperation(op,second, first);
         //   Console.WriteLine("{0} {1} {2} = {3}", first, op, second, result);
            basket.Push( result.ToString());
        }
        else if (IsAnOperator(postfixtokens[j][0]) && basket.Count == 1)
        {

            Double second = Double.Parse(basket.Pop());
            Double first = 0.0;
            char op = postfixtokens[j][0];

            Double result = ApplyOperation(op, second, first);

         //   Console.WriteLine("{0} {1} {2} = {3}", first, op, second, result);
            basket.Push(result.ToString() );
        }
        else if (Char.IsDigit(postfixtokens[j][0]))
        {
            basket.Push( postfixtokens[j] );
        }
        else if( isAFunction(postfixtokens[j]) )
        {
            //if its a single argument function
            if (AllowedFunctions[postfixtokens[j]] == 1)
            {
                //single arg logic
                if (postfixtokens[j] == "sin")
                {
                    Double result =  Math.Sin( Double.Parse(basket.Pop() )*Math.PI/180.0 );
                    //result = Math.PI / 180;
                    basket.Push(result.ToString());
                }
                else if (postfixtokens[j] == "cos")
                {
                       Double result =  Math.Cos( Double.Parse(basket.Pop() )*Math.PI/180.0 );
                    //result = Math.PI / 180;
                    basket.Push(result.ToString());
                }

            }

        }
    }
}

此外,这是程序的输出:

   Input: 3+4*2/(1-5)^5^10
   PostFix: 3 4 2 * 1 5 - 5 10 ^ ^ / +
   Answer: 3


   Input: 2+4
   PostFix: 2 4 +
   Answer: 6

   Input Expression: -5-4
   Input: -5-4
   PostFix: 5 - 4 -
   Answer: -9

   Input: -4+3
   PostFix: 4 - 3 +
   Answer: -1

   Input Expression: 4^(4^4)
   Input: 4^(4^4)
   PostFix: 4 4 4 ^ ^
   Answer: 1.34078079299426E+154

   Input: sin45
   PostFix: 45 sin
   Answer: 0.707106781186547  (correct)

//错误的

   Input: sin(25)+cos(15+sin(25))+3
   PostFix: 25 15 25 sin + 3 + cos + sin
   Answer: 0.437567038002202

   Input: sin(45)+cos(45)
   PostFix: 45 45 cos + sin
   Answer: 0.71577935734684

新案例:

    Input: sin45+cos45
    PostFix: 45 45 cos + sin
    Answer: 0.71577935734684

    Input: 2+sin30
    PostFix: 2 30 sin +
    Answer:2.5

    Input: sin30+2
    PostFix: 30 2 + sin
    Answer: 0.529919264233205

就是这样。任何人都可以指出我做错了什么。

编辑:

这是 IsHigherPrecedance 函数和优先级枚举:

    public enum Precedance { Plus =1,Minus=1,Multiply=2,Divide=2,Exponent=3,Unary = 4,Parenthesis=5 };
   private bool IsHigherPrecedence(char a, char b)
   {
    Precedance f = getPrecedance(a);
    Precedance s = getPrecedance(b);


    if (f >= s)
        return false;
    else
        return true;
   }
  public Precedance getPrecedance(char a)
{

    if (a == '+')
        return Precedance.Plus;
    else if (a == '-')
        return Precedance.Minus;
    else if (a == '*')
        return Precedance.Multiply;
    else if (a == '/')
        return Precedance.Divide;
    else if (a == '^')
        return Precedance.Exponent;
    else if (Char.IsLetter(a))
        return Precedance.Unary;
    else if (a == '(' || a == ')')
        return Precedance.Parenthesis;
    else
        return Precedance.Plus;
}

既然这些三角函数是单参数函数,它们是否会用其他逻辑进行解析,或者这个分流场算法也适用于这些函数?

问候。

4

2 回答 2

3

这里有很多问题,但主要问题是您将函数视为运算符,尽管它们不是(本质上是您将堆栈称为“运算符”,就好像这是唯一可以在上面的东西, 不对)。特别是,这个分支:

else if (IsAnOperator(input[j])) // if we have got an operator
{
    if (operators.Count != 0 && IsHigherPrecedence(operators.Peek().ToCharArray()[0], input[j]))
    {
        while ( ( operators.Count != 0 && IsHigherPrecedence(operators.Peek().ToCharArray()[0], input[j]) )  )
        { 
            postfix += operators.Pop() + " "; 
        }
    }
    operators.Push(Char.ToString(input[j]));
}

需要检查“操作员”堆栈上的内容是否实际上是操作员:

else if (IsAnOperator(input[j])) // if we have got an operator
{
    while (operators.Count != 0 
        && IsAnOperator(operators.Peek().ToCharArray()[0])
        && IsHigherPrecedence(operators.Peek().ToCharArray()[0], input[j]))
    {
       postfix += operators.Pop() + " "; 
    }
    operators.Push(Char.ToString(input[j]));
}

其他问题包括处理逗号的分支:

else if (input[j] == ',') //if its a comma
{
    while (operators.Peek() != "(")
    {
        // this isnt right, but its not the problem
        postfix += input[j] + " ";
        // should be this:
        postfix += operators.Pop() + " ";
    }

}
于 2012-11-26T02:07:09.403 回答
0

如果你使用这个库:https ://github.com/leblancmeneses/NPEG 你可以使用这个语法来解析你的表达式。

Digit: (?<Digit>[0-9]+('.'[0-9]+)?);
(?<Trig>): 'sin(' Expr ')' / 'cos(' Expr ')';
Value:  Digit / Trig / '(' Expr ')';
(?<Product>): Value ((?<Symbol>'*' / '/') Value)*;
(?<Sum>): Product ((?<Symbol>'+' / '-') Product)*;
(?<Expr>): Sum ;

在这里测试一下: http ://www.robusthaven.com/blog/parsing-expression-grammar/npeg-language-workbench

cos(25)+cos(15+sin(25.333))+3

((((12/3)+5-2*(81/9))+1))

核桃:

安装包 NPEG

查看布尔代数的 AST 示例评估。

于 2012-11-26T01:01:55.987 回答