Writing a simple equation parser

前端 未结 10 1152
-上瘾入骨i
-上瘾入骨i 2020-12-01 03:56

What sorts of algorithms would be used to do this (as in, this is a string, and I want to find the answer):

((5 + (3 + (7 * 2))) - (8 * 9)) / 72
相关标签:
10条回答
  • 2020-12-01 04:23

    Or you can just do this in one line in R:

    > eval(parse(text = '((5 + (3 + (7*2))) - (8 * 9))/72' ))
    [1] -0.6944444
    
    0 讨论(0)
  • 2020-12-01 04:29

    I would use the tools that are available nearly everywhere.
    I like lex/yacc because I know them but there are equivalents everywhere. So before you write complex code see if there are tools that can help you to make it simple (problems like this have been solved before so don;t re-invent the wheel).

    So, using lex(flex)/yacc(bison) I would do:

    e.l

    %option noyywrap
    
    Number      [0-9]+
    WhiteSpace  [ \t\v\r]+
    NewLine     \n
    %{
    #include <stdio.h>
    %}
    
    %%
    
    \(              return '(';
    \)              return ')';
    \+              return '+';
    \-              return '-';
    \*              return '*';
    \/              return '/';
    
    {Number}        return 'N';
    {NewLine}       return '\n';
    {WhiteSpace}    /* Ignore */
    
    .               fprintf(stdout,"Error\n");exit(1);
    
    
    %%
    

    e.y

    %{
    #include <stdio.h>
        typedef double (*Operator)(double,double);
        double mulOp(double l,double r)  {return l*r;}
        double divOp(double l,double r)  {return l/r;}
        double addOp(double l,double r)  {return l+r;}
        double subOp(double l,double r)  {return l-r;}
    extern char* yytext;
    extern void yyerror(char const * msg);
    %}
    
    %union          
    {
        Operator        op;
        double          value;
    }
    
    %type   <op>        MultOp AddOp
    %type   <value>     Expression MultExpr AddExpr BraceExpr
    
    %%
    
    Value:          Expression '\n'   { fprintf(stdout, "Result: %le\n", $1);return 0; }
    
    Expression:     AddExpr                          { $$ = $1;}
    
    AddExpr:        MultExpr                         { $$ = $1;}
                |   AddExpr   AddOp  MultExpr        { $$ = ($2)($1, $3);}
    
    MultExpr:       BraceExpr                        { $$ = $1;}
                |   MultExpr  MultOp BraceExpr       { $$ = ($2)($1, $3);}
    
    BraceExpr:      '(' Expression ')'               { $$ = $2;}
                |   'N'                              { sscanf(yytext,"%le", &$$);}
    
    MultOp:         '*'                              { $$ = &mulOp;}
                |   '/'                              { $$ = &divOp;}
    AddOp:          '+'                              { $$ = &addOp;}
                |   '-'                              { $$ = &subOp;}
    %%
    
    void yyerror(char const * msg)
    {
        fprintf(stdout,"Error: %s", msg);
    }
     
    int main()
    {
        yyparse();
    }
    

    Build

    > flex e.l
    > bison e.y
    > gcc *.c
    > ./a.out
    ((5 + (3 + (7 * 2))) - (8 * 9)) / 72
    Result: -6.944444e-01
    >
    

    The above also handles normal operator precedence rules:
    Not because of anything I did,but somebody smart worked this out ages ago and now you can get the grammar rules for expression parsing easily (Just google C Grammer and rip the bit you need out).

    > ./a.out
    2 + 3 * 4
    Result: 1.400000e+01
    
    0 讨论(0)
  • 2020-12-01 04:30

    James has provided a good answer. Wikipedia has a good article on this as well.

    If (and I don't recommend this) you wanted to parse that expression directly, given that it seems orderly in that every set of parens has no more than one pair of operands, I think you could approach it like this:

    parse to the first ")". Then parse back to the previous "(". Evaluate what's inside and replace the whole set with a value. Then repeat recursively until you are done.

    So in this example, you would first parse "(7 * 2)" and replace it with 14. Then you would get (3 + 14) and replace it with 17. And so on.

    You can do that with Regex or even .IndexOf and .Substring.

    I'm going without benefit of checking my syntax here, but something like this:

    int y = string.IndexOf(")");  
    int x = string.Substring(0,y).LastIndexOf("(");  
    string z = string.Substring(x+1,y-x-1) // This should result in "7 * 2"
    

    You'll need to evaluate the resulting expression and loop this until the parens are exhausted and then evaluate that last part of the string.

    0 讨论(0)
  • 2020-12-01 04:30

    What? Nooooo. Unless this is a homework assignment, do not write a parser. There are a hundred parsers out there and they all have one advantage over all the suggestions here: they're already out there. You don't have to write them.

    0 讨论(0)
  • 2020-12-01 04:31

    Yes the algorithm is Shunting yard algorithm but if you want to implement I suggest you python and it's compiler package

    import compiler
    equation = "((5 + (3 + (7 * 2))) - (8 * 9)) / 72"
    parsed = compiler.parse( equation )
    
    print parsed
    

    You can also evaluate this expression with built-in eval() method

    print eval("5 + (4./3) * 9") // 17
    
    0 讨论(0)
  • 2020-12-01 04:34

    First convert the expression into prefix or postfix form. Then its very easy to evaluate!

    Example:

    Postfix expression evaluation.

    0 讨论(0)
提交回复
热议问题