具有三角函数的调车场算法

时间:2012-11-23 15:53:57

标签: c# parsing mathematical-expressions shunting-yard

我正在C#中实现分流码算法。虽然它很好地用符号(+,* - /和^)解析数学表达式。但由于某种原因它不适用于正弦余弦函数。例如,如果我尝试计算罪(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)

我已按照本文at 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函数和precedance枚举 :

    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;
}

既然这些三角函数是单个参数函数,它们是否会被其他一些逻辑解析,或者这个调车场算法是否也可以使用这些函数?

问候。

2 个答案:

答案 0 :(得分: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() + " ";
    }

}

答案 1 :(得分: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))

nuget:

安装包NPEG

查看布尔代数的AST样本评估。