Recursive math expression evalNumeric expression parser - calculatorMath expression solverRecursive...

What wound would be of little consequence to a biped but terrible for a quadruped?

Why does the negative sign arise in this thermodynamic relation?

Does "variables should live in the smallest scope as possible" include the case "variables should not exist if possible"?

Who deserves to be first and second author? PhD student who collected data, research associate who wrote the paper or supervisor?

Is there an elementary proof that there are infinitely many primes that are *not* completely split in an abelian extension?

Aliens englobed the Solar System: will we notice?

How strictly should I take "Candidates must be local"?

Try Catch Block Affecting a Variable in an Enclosing Scope

Do I really need to have a scientific explanation for my premise?

Do items de-spawn in Diablo?

How did the power source of Mar-Vell's aircraft end up with her?

How can I ensure my trip to the UK will not have to be cancelled because of Brexit?

If the Captain's screens are out, does he switch seats with the co-pilot?

How to pass a string to a command that expects a file?

Tricky AM-GM inequality

Why doesn't this Google Translate ad use the word "Translation" instead of "Translate"?

Set and print content of environment variable in cmd.exe subshell?

Single word request: Harming the benefactor

Should I tell my boss the work he did was worthless

Offered promotion but I'm leaving. Should I tell?

In the late 1940’s to early 1950’s what technology was available that could melt a LOT of ice?

Good allowance savings plan?

Is "history" a male-biased word ("his+story")?

Are babies of evil humanoid species inherently evil?



Recursive math expression eval


Numeric expression parser - calculatorMath expression solverRecursive calculator in CPredictive recursive descent parser for math expressionsMath expression parser in C#Math expression on-the-fly interpreterMath eval function for HaskellMath expression parser in JavaScriptMath expression evaluatorCalculator/math expression parser (supporting complex numbers)













4












$begingroup$


It has been very hard to use recursion, but I think that it made the code shorter and cleaner.



import doctest
import operator as op

START_FOR_SYMBOLS = {'+': 0,
'*': 1,
'/':1,
'-':0
}

OP_FOR_SYMBOL = {'+': op.add,
'*': op.mul,
'/': op.truediv,
'-': op.sub
}


def innermost_parens(text):
"""
Returns the text inside the innermost parenthesis.

>>> innermost_parens("1 + (2 * (4 - 1))")
'4 - 1'
>>> innermost_parens("1 + (2 * (4 * (2 + (8 * 7)) - 1))")
'8 * 7'
"""
if not '(' in text and not ')' in text:
return text
open_ = text.index('(')
close_ = text.rindex(')')
return innermost_parens(text[open_+1:close_])

def polish_eval(expr,start=None):
"""
Unlimited polish eval, works for any number of arguments.

>>> polish_eval('+ 4 1 6')
11.0
>>> polish_eval('* 4 5 5')
100.0
"""
tokens = expr.split(' ')
if start is None:
start = START_FOR_SYMBOLS[tokens[0]]
if len(tokens) == 1:
return start
return polish_eval(' '.join(tokens[:-1]),
start = OP_FOR_SYMBOL[tokens[0]](start,float(tokens[-1]))
)

def infix_eval(expr):
"""
Reduced infix eval, only works with 2 numbers.

>>> infix_eval('9 + 4')
13.0
>>> infix_eval('2 * -6')
-12.0
"""
a, oper, b = expr.split()
return OP_FOR_SYMBOL[oper](float(a),float(b))

def full_eval(expr, eval_type):
"""
Evals by the rules of eval_type starting from the inner
parenthesis.

>>> full_eval("(* 4 5 (+ 4 1))", polish_eval)
100.0
>>> full_eval("(* 4 (/ 10))", polish_eval)
0.4
>>> full_eval("(1 + (5 * 2))", infix_eval)
11.0
"""
if len(expr.split(' ')) == 1:
return float(expr)
inn = innermost_parens(expr)
new_expr = expr.replace('('+str(inn)+')',str(eval_type(inn)))
return full_eval(new_expr, eval_type)

def interface():
which_expr = input("Polish or infix? ")
if 'polish' in which_expr.lower():
evaller = lambda expr: full_eval(expr, polish_eval)
else:
evaller = lambda expr: full_eval(expr, infix_eval)
while True:
result = evaller(input('> '))
print(result)

if __name__ == "__main__":
doctest.testmod()
interface()









share|improve this question









$endgroup$








  • 2




    $begingroup$
    How would you handle the case '- 20 5 3' for Polish notation? Your program returns '-28'. My suggestion is that you restrict the number of numbers (no pun intended) allowed to just 2; this makes the job much easier and allows people to write Polish notation without brackets.
    $endgroup$
    – wei2912
    Apr 9 '15 at 13:11
















4












$begingroup$


It has been very hard to use recursion, but I think that it made the code shorter and cleaner.



import doctest
import operator as op

START_FOR_SYMBOLS = {'+': 0,
'*': 1,
'/':1,
'-':0
}

OP_FOR_SYMBOL = {'+': op.add,
'*': op.mul,
'/': op.truediv,
'-': op.sub
}


def innermost_parens(text):
"""
Returns the text inside the innermost parenthesis.

>>> innermost_parens("1 + (2 * (4 - 1))")
'4 - 1'
>>> innermost_parens("1 + (2 * (4 * (2 + (8 * 7)) - 1))")
'8 * 7'
"""
if not '(' in text and not ')' in text:
return text
open_ = text.index('(')
close_ = text.rindex(')')
return innermost_parens(text[open_+1:close_])

def polish_eval(expr,start=None):
"""
Unlimited polish eval, works for any number of arguments.

>>> polish_eval('+ 4 1 6')
11.0
>>> polish_eval('* 4 5 5')
100.0
"""
tokens = expr.split(' ')
if start is None:
start = START_FOR_SYMBOLS[tokens[0]]
if len(tokens) == 1:
return start
return polish_eval(' '.join(tokens[:-1]),
start = OP_FOR_SYMBOL[tokens[0]](start,float(tokens[-1]))
)

def infix_eval(expr):
"""
Reduced infix eval, only works with 2 numbers.

>>> infix_eval('9 + 4')
13.0
>>> infix_eval('2 * -6')
-12.0
"""
a, oper, b = expr.split()
return OP_FOR_SYMBOL[oper](float(a),float(b))

def full_eval(expr, eval_type):
"""
Evals by the rules of eval_type starting from the inner
parenthesis.

>>> full_eval("(* 4 5 (+ 4 1))", polish_eval)
100.0
>>> full_eval("(* 4 (/ 10))", polish_eval)
0.4
>>> full_eval("(1 + (5 * 2))", infix_eval)
11.0
"""
if len(expr.split(' ')) == 1:
return float(expr)
inn = innermost_parens(expr)
new_expr = expr.replace('('+str(inn)+')',str(eval_type(inn)))
return full_eval(new_expr, eval_type)

def interface():
which_expr = input("Polish or infix? ")
if 'polish' in which_expr.lower():
evaller = lambda expr: full_eval(expr, polish_eval)
else:
evaller = lambda expr: full_eval(expr, infix_eval)
while True:
result = evaller(input('> '))
print(result)

if __name__ == "__main__":
doctest.testmod()
interface()









share|improve this question









$endgroup$








  • 2




    $begingroup$
    How would you handle the case '- 20 5 3' for Polish notation? Your program returns '-28'. My suggestion is that you restrict the number of numbers (no pun intended) allowed to just 2; this makes the job much easier and allows people to write Polish notation without brackets.
    $endgroup$
    – wei2912
    Apr 9 '15 at 13:11














4












4








4


2



$begingroup$


It has been very hard to use recursion, but I think that it made the code shorter and cleaner.



import doctest
import operator as op

START_FOR_SYMBOLS = {'+': 0,
'*': 1,
'/':1,
'-':0
}

OP_FOR_SYMBOL = {'+': op.add,
'*': op.mul,
'/': op.truediv,
'-': op.sub
}


def innermost_parens(text):
"""
Returns the text inside the innermost parenthesis.

>>> innermost_parens("1 + (2 * (4 - 1))")
'4 - 1'
>>> innermost_parens("1 + (2 * (4 * (2 + (8 * 7)) - 1))")
'8 * 7'
"""
if not '(' in text and not ')' in text:
return text
open_ = text.index('(')
close_ = text.rindex(')')
return innermost_parens(text[open_+1:close_])

def polish_eval(expr,start=None):
"""
Unlimited polish eval, works for any number of arguments.

>>> polish_eval('+ 4 1 6')
11.0
>>> polish_eval('* 4 5 5')
100.0
"""
tokens = expr.split(' ')
if start is None:
start = START_FOR_SYMBOLS[tokens[0]]
if len(tokens) == 1:
return start
return polish_eval(' '.join(tokens[:-1]),
start = OP_FOR_SYMBOL[tokens[0]](start,float(tokens[-1]))
)

def infix_eval(expr):
"""
Reduced infix eval, only works with 2 numbers.

>>> infix_eval('9 + 4')
13.0
>>> infix_eval('2 * -6')
-12.0
"""
a, oper, b = expr.split()
return OP_FOR_SYMBOL[oper](float(a),float(b))

def full_eval(expr, eval_type):
"""
Evals by the rules of eval_type starting from the inner
parenthesis.

>>> full_eval("(* 4 5 (+ 4 1))", polish_eval)
100.0
>>> full_eval("(* 4 (/ 10))", polish_eval)
0.4
>>> full_eval("(1 + (5 * 2))", infix_eval)
11.0
"""
if len(expr.split(' ')) == 1:
return float(expr)
inn = innermost_parens(expr)
new_expr = expr.replace('('+str(inn)+')',str(eval_type(inn)))
return full_eval(new_expr, eval_type)

def interface():
which_expr = input("Polish or infix? ")
if 'polish' in which_expr.lower():
evaller = lambda expr: full_eval(expr, polish_eval)
else:
evaller = lambda expr: full_eval(expr, infix_eval)
while True:
result = evaller(input('> '))
print(result)

if __name__ == "__main__":
doctest.testmod()
interface()









share|improve this question









$endgroup$




It has been very hard to use recursion, but I think that it made the code shorter and cleaner.



import doctest
import operator as op

START_FOR_SYMBOLS = {'+': 0,
'*': 1,
'/':1,
'-':0
}

OP_FOR_SYMBOL = {'+': op.add,
'*': op.mul,
'/': op.truediv,
'-': op.sub
}


def innermost_parens(text):
"""
Returns the text inside the innermost parenthesis.

>>> innermost_parens("1 + (2 * (4 - 1))")
'4 - 1'
>>> innermost_parens("1 + (2 * (4 * (2 + (8 * 7)) - 1))")
'8 * 7'
"""
if not '(' in text and not ')' in text:
return text
open_ = text.index('(')
close_ = text.rindex(')')
return innermost_parens(text[open_+1:close_])

def polish_eval(expr,start=None):
"""
Unlimited polish eval, works for any number of arguments.

>>> polish_eval('+ 4 1 6')
11.0
>>> polish_eval('* 4 5 5')
100.0
"""
tokens = expr.split(' ')
if start is None:
start = START_FOR_SYMBOLS[tokens[0]]
if len(tokens) == 1:
return start
return polish_eval(' '.join(tokens[:-1]),
start = OP_FOR_SYMBOL[tokens[0]](start,float(tokens[-1]))
)

def infix_eval(expr):
"""
Reduced infix eval, only works with 2 numbers.

>>> infix_eval('9 + 4')
13.0
>>> infix_eval('2 * -6')
-12.0
"""
a, oper, b = expr.split()
return OP_FOR_SYMBOL[oper](float(a),float(b))

def full_eval(expr, eval_type):
"""
Evals by the rules of eval_type starting from the inner
parenthesis.

>>> full_eval("(* 4 5 (+ 4 1))", polish_eval)
100.0
>>> full_eval("(* 4 (/ 10))", polish_eval)
0.4
>>> full_eval("(1 + (5 * 2))", infix_eval)
11.0
"""
if len(expr.split(' ')) == 1:
return float(expr)
inn = innermost_parens(expr)
new_expr = expr.replace('('+str(inn)+')',str(eval_type(inn)))
return full_eval(new_expr, eval_type)

def interface():
which_expr = input("Polish or infix? ")
if 'polish' in which_expr.lower():
evaller = lambda expr: full_eval(expr, polish_eval)
else:
evaller = lambda expr: full_eval(expr, infix_eval)
while True:
result = evaller(input('> '))
print(result)

if __name__ == "__main__":
doctest.testmod()
interface()






python recursion math-expression-eval






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Apr 9 '15 at 12:47









CaridorcCaridorc

23k538117




23k538117








  • 2




    $begingroup$
    How would you handle the case '- 20 5 3' for Polish notation? Your program returns '-28'. My suggestion is that you restrict the number of numbers (no pun intended) allowed to just 2; this makes the job much easier and allows people to write Polish notation without brackets.
    $endgroup$
    – wei2912
    Apr 9 '15 at 13:11














  • 2




    $begingroup$
    How would you handle the case '- 20 5 3' for Polish notation? Your program returns '-28'. My suggestion is that you restrict the number of numbers (no pun intended) allowed to just 2; this makes the job much easier and allows people to write Polish notation without brackets.
    $endgroup$
    – wei2912
    Apr 9 '15 at 13:11








2




2




$begingroup$
How would you handle the case '- 20 5 3' for Polish notation? Your program returns '-28'. My suggestion is that you restrict the number of numbers (no pun intended) allowed to just 2; this makes the job much easier and allows people to write Polish notation without brackets.
$endgroup$
– wei2912
Apr 9 '15 at 13:11




$begingroup$
How would you handle the case '- 20 5 3' for Polish notation? Your program returns '-28'. My suggestion is that you restrict the number of numbers (no pun intended) allowed to just 2; this makes the job much easier and allows people to write Polish notation without brackets.
$endgroup$
– wei2912
Apr 9 '15 at 13:11










2 Answers
2






active

oldest

votes


















5












$begingroup$

Good job on adding the test suites; they make testing code extremely easy.



Recursion often results in a clean solution. However, I think that this problem can be solved just as well with an iterative approach.



The key to innermost_parens is to return the text between the last ( and the first ). Recursion is a viable solution, but I find that this for loop does the job better and in a cleaner way:



def innermost_parens(text):
"""
Returns the innermost parenthesis.

>>> innermost_parens('* 1 2')
'* 1 2'
>>> innermost_parens("1 + (2 * (4 - 1))")
'(4 - 1)'
>>> innermost_parens("1 + (2 * (4 * (2 + (8 * 7)) - 1))")
'(8 * 7)'
>>> innermost_parens("(1 + 2) * (3 + 4)")
'(1 + 2)'
"""
start = 0
end = len(text)
for (i, c) in enumerate(text):
if c == '(':
start = i
if c == ')':
end = i + 1
break
return text[start:end]


Take note that this function will return the first innermost expression with brackets. The reason for this will be clear later.



As for polish_eval, your program doesn't follow the standard Polish notation. In the standard Polish notation you can only perform operations on two numbers. It is also unclear what 'start' does. I modified your code to do the above and added tests for subtraction and division:



def polish_eval(expr):
"""
Reduced Polish notation.

>>> polish_eval('+ 4 1')
5.0
>>> polish_eval('* 4 5')
20.0
>>> polish_eval('- 20 3')
17.0
>>> polish_eval('/ 20 5')
4.0
"""
oper, a, b = expr.split()
return OP_FOR_SYMBOL[oper](float(a), float(b))


I'll leave the documentation of the Polish notation to you. No changes were made to infix_eval.



Finally, we're on to full_eval. Since there's been a major change in the notation we support, we'll have to rewrite the code. First, to start off with the tests:



>>> full_eval("* (* 4 5) (+ 4 1)", polish_eval)
100.0
>>> full_eval("* 4 (/ 1 10)", polish_eval)
0.4
>>> full_eval("1 + (5 * 2)", infix_eval)
11.0


Rewriting Polish notation without brackets would require the use of a stack. This will not fit nicely in your full_eval function, so I will not implement the notation without brackets. In this function, the use of recursion is unnecessary and a while loop will fit the problem nicely.



def full_eval(expr, eval_type):
"""
Evals by the rules of eval_type starting from the inner
parenthesis.

>>> full_eval("* (* 4 5) (+ 4 1)", polish_eval)
100.0
>>> full_eval("* 4 (/ 1 10)", polish_eval)
0.4
>>> full_eval("1 + (5 * 2)", infix_eval)
11.0
"""
while len(expr.split()) != 1:
inn = innermost_parens(expr)
result = eval_type(inn.strip('(').strip(')').strip())
expr = expr.replace(inn, str(result))
return float(expr)


Before I evaluate inn, I first strip off the brackets which enclose the expression, as well as any remaining whitespace.



The reason why I wrote innermost_parens as shown above is to allow for the easy replacement of the expression. Here's a trace of an expression in Polish:



  * (/ 9 3) (+ (/ 3 5) (- 20 5))
= * (/ 9 3) (+ 0.6 (- 20 5)) # '(/ 3 5)' -> '0.6'
= * (/ 9 3) (+ 0.6 15.0) # '(- 20 5)' -> '15.0'
= * 3.0 (+ 0.6 15.0) # '(/ 9 3)' -> '3.0'
= * 3.0 15.6 # '(+ 0.6 15.0)' -> '15.6'
= 46.8 # '* 3.0 15.6' -> '46.8'


and the same expression in infix:



  (9 / 3) * ((3 / 5) + (20 - 5))
= (9 / 3) * (0.6 + (20 - 5)) # '(3 / 5)' -> '0.6'
= (9 / 3) * (0.6 + 15.0) # '(20 - 5)' -> '15.0'
= 3.0 * (0.6 + 15.0) # '(9 / 3)' -> '3.0'
= 3.0 * 15.6 # '(0.6 + 15.0)' -> '15.6'
= 46.8 # '3.0 * 15.6' -> '46.8'




Extensions:




  1. Write your program such that it can parse Polish notation without brackets. That's what Polish notation is invented for.

  2. Handle edge cases such as ((* 3 15)) (Polish).

  3. Write test suites to ensure that the expressions are evaluated correctly. While addition and multiplication is commutative, subtraction and division are not, and the order of the numbers matter.

  4. Follow suggestions in l0b0's answer, which are cosmetic. I did not make any cosmetic suggestions as I thought that it was more important to settle the logic of the program first.

  5. Have your program create an abstract syntax tree instead of doing string operations (such as replacing innermost expression with its value).


Feel free to ask questions in the comments.






share|improve this answer











$endgroup$





















    2












    $begingroup$

    Some suggestions:




    • Rather than ask whether all the following expressions are going to be in Polish or infix notation, you can simply check whether the first non-parenthesis character is a number or an operator.


    • START_FOR_SYMBOLS could be renamed to OPERATION_IDENTITIES for mathematical clarity.


    Open issues:




    • Why doctest.testmod() on every run?

    • There isn't always a unique innermost parenthesis, for example in the case of (1 + 2) * (3 + 4). You probably want to return a list and process each of them in turn.






    share|improve this answer









    $endgroup$













      Your Answer





      StackExchange.ifUsing("editor", function () {
      return StackExchange.using("mathjaxEditing", function () {
      StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
      StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
      });
      });
      }, "mathjax-editing");

      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "196"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: false,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: null,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f86389%2frecursive-math-expression-eval%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      2 Answers
      2






      active

      oldest

      votes








      2 Answers
      2






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      5












      $begingroup$

      Good job on adding the test suites; they make testing code extremely easy.



      Recursion often results in a clean solution. However, I think that this problem can be solved just as well with an iterative approach.



      The key to innermost_parens is to return the text between the last ( and the first ). Recursion is a viable solution, but I find that this for loop does the job better and in a cleaner way:



      def innermost_parens(text):
      """
      Returns the innermost parenthesis.

      >>> innermost_parens('* 1 2')
      '* 1 2'
      >>> innermost_parens("1 + (2 * (4 - 1))")
      '(4 - 1)'
      >>> innermost_parens("1 + (2 * (4 * (2 + (8 * 7)) - 1))")
      '(8 * 7)'
      >>> innermost_parens("(1 + 2) * (3 + 4)")
      '(1 + 2)'
      """
      start = 0
      end = len(text)
      for (i, c) in enumerate(text):
      if c == '(':
      start = i
      if c == ')':
      end = i + 1
      break
      return text[start:end]


      Take note that this function will return the first innermost expression with brackets. The reason for this will be clear later.



      As for polish_eval, your program doesn't follow the standard Polish notation. In the standard Polish notation you can only perform operations on two numbers. It is also unclear what 'start' does. I modified your code to do the above and added tests for subtraction and division:



      def polish_eval(expr):
      """
      Reduced Polish notation.

      >>> polish_eval('+ 4 1')
      5.0
      >>> polish_eval('* 4 5')
      20.0
      >>> polish_eval('- 20 3')
      17.0
      >>> polish_eval('/ 20 5')
      4.0
      """
      oper, a, b = expr.split()
      return OP_FOR_SYMBOL[oper](float(a), float(b))


      I'll leave the documentation of the Polish notation to you. No changes were made to infix_eval.



      Finally, we're on to full_eval. Since there's been a major change in the notation we support, we'll have to rewrite the code. First, to start off with the tests:



      >>> full_eval("* (* 4 5) (+ 4 1)", polish_eval)
      100.0
      >>> full_eval("* 4 (/ 1 10)", polish_eval)
      0.4
      >>> full_eval("1 + (5 * 2)", infix_eval)
      11.0


      Rewriting Polish notation without brackets would require the use of a stack. This will not fit nicely in your full_eval function, so I will not implement the notation without brackets. In this function, the use of recursion is unnecessary and a while loop will fit the problem nicely.



      def full_eval(expr, eval_type):
      """
      Evals by the rules of eval_type starting from the inner
      parenthesis.

      >>> full_eval("* (* 4 5) (+ 4 1)", polish_eval)
      100.0
      >>> full_eval("* 4 (/ 1 10)", polish_eval)
      0.4
      >>> full_eval("1 + (5 * 2)", infix_eval)
      11.0
      """
      while len(expr.split()) != 1:
      inn = innermost_parens(expr)
      result = eval_type(inn.strip('(').strip(')').strip())
      expr = expr.replace(inn, str(result))
      return float(expr)


      Before I evaluate inn, I first strip off the brackets which enclose the expression, as well as any remaining whitespace.



      The reason why I wrote innermost_parens as shown above is to allow for the easy replacement of the expression. Here's a trace of an expression in Polish:



        * (/ 9 3) (+ (/ 3 5) (- 20 5))
      = * (/ 9 3) (+ 0.6 (- 20 5)) # '(/ 3 5)' -> '0.6'
      = * (/ 9 3) (+ 0.6 15.0) # '(- 20 5)' -> '15.0'
      = * 3.0 (+ 0.6 15.0) # '(/ 9 3)' -> '3.0'
      = * 3.0 15.6 # '(+ 0.6 15.0)' -> '15.6'
      = 46.8 # '* 3.0 15.6' -> '46.8'


      and the same expression in infix:



        (9 / 3) * ((3 / 5) + (20 - 5))
      = (9 / 3) * (0.6 + (20 - 5)) # '(3 / 5)' -> '0.6'
      = (9 / 3) * (0.6 + 15.0) # '(20 - 5)' -> '15.0'
      = 3.0 * (0.6 + 15.0) # '(9 / 3)' -> '3.0'
      = 3.0 * 15.6 # '(0.6 + 15.0)' -> '15.6'
      = 46.8 # '3.0 * 15.6' -> '46.8'




      Extensions:




      1. Write your program such that it can parse Polish notation without brackets. That's what Polish notation is invented for.

      2. Handle edge cases such as ((* 3 15)) (Polish).

      3. Write test suites to ensure that the expressions are evaluated correctly. While addition and multiplication is commutative, subtraction and division are not, and the order of the numbers matter.

      4. Follow suggestions in l0b0's answer, which are cosmetic. I did not make any cosmetic suggestions as I thought that it was more important to settle the logic of the program first.

      5. Have your program create an abstract syntax tree instead of doing string operations (such as replacing innermost expression with its value).


      Feel free to ask questions in the comments.






      share|improve this answer











      $endgroup$


















        5












        $begingroup$

        Good job on adding the test suites; they make testing code extremely easy.



        Recursion often results in a clean solution. However, I think that this problem can be solved just as well with an iterative approach.



        The key to innermost_parens is to return the text between the last ( and the first ). Recursion is a viable solution, but I find that this for loop does the job better and in a cleaner way:



        def innermost_parens(text):
        """
        Returns the innermost parenthesis.

        >>> innermost_parens('* 1 2')
        '* 1 2'
        >>> innermost_parens("1 + (2 * (4 - 1))")
        '(4 - 1)'
        >>> innermost_parens("1 + (2 * (4 * (2 + (8 * 7)) - 1))")
        '(8 * 7)'
        >>> innermost_parens("(1 + 2) * (3 + 4)")
        '(1 + 2)'
        """
        start = 0
        end = len(text)
        for (i, c) in enumerate(text):
        if c == '(':
        start = i
        if c == ')':
        end = i + 1
        break
        return text[start:end]


        Take note that this function will return the first innermost expression with brackets. The reason for this will be clear later.



        As for polish_eval, your program doesn't follow the standard Polish notation. In the standard Polish notation you can only perform operations on two numbers. It is also unclear what 'start' does. I modified your code to do the above and added tests for subtraction and division:



        def polish_eval(expr):
        """
        Reduced Polish notation.

        >>> polish_eval('+ 4 1')
        5.0
        >>> polish_eval('* 4 5')
        20.0
        >>> polish_eval('- 20 3')
        17.0
        >>> polish_eval('/ 20 5')
        4.0
        """
        oper, a, b = expr.split()
        return OP_FOR_SYMBOL[oper](float(a), float(b))


        I'll leave the documentation of the Polish notation to you. No changes were made to infix_eval.



        Finally, we're on to full_eval. Since there's been a major change in the notation we support, we'll have to rewrite the code. First, to start off with the tests:



        >>> full_eval("* (* 4 5) (+ 4 1)", polish_eval)
        100.0
        >>> full_eval("* 4 (/ 1 10)", polish_eval)
        0.4
        >>> full_eval("1 + (5 * 2)", infix_eval)
        11.0


        Rewriting Polish notation without brackets would require the use of a stack. This will not fit nicely in your full_eval function, so I will not implement the notation without brackets. In this function, the use of recursion is unnecessary and a while loop will fit the problem nicely.



        def full_eval(expr, eval_type):
        """
        Evals by the rules of eval_type starting from the inner
        parenthesis.

        >>> full_eval("* (* 4 5) (+ 4 1)", polish_eval)
        100.0
        >>> full_eval("* 4 (/ 1 10)", polish_eval)
        0.4
        >>> full_eval("1 + (5 * 2)", infix_eval)
        11.0
        """
        while len(expr.split()) != 1:
        inn = innermost_parens(expr)
        result = eval_type(inn.strip('(').strip(')').strip())
        expr = expr.replace(inn, str(result))
        return float(expr)


        Before I evaluate inn, I first strip off the brackets which enclose the expression, as well as any remaining whitespace.



        The reason why I wrote innermost_parens as shown above is to allow for the easy replacement of the expression. Here's a trace of an expression in Polish:



          * (/ 9 3) (+ (/ 3 5) (- 20 5))
        = * (/ 9 3) (+ 0.6 (- 20 5)) # '(/ 3 5)' -> '0.6'
        = * (/ 9 3) (+ 0.6 15.0) # '(- 20 5)' -> '15.0'
        = * 3.0 (+ 0.6 15.0) # '(/ 9 3)' -> '3.0'
        = * 3.0 15.6 # '(+ 0.6 15.0)' -> '15.6'
        = 46.8 # '* 3.0 15.6' -> '46.8'


        and the same expression in infix:



          (9 / 3) * ((3 / 5) + (20 - 5))
        = (9 / 3) * (0.6 + (20 - 5)) # '(3 / 5)' -> '0.6'
        = (9 / 3) * (0.6 + 15.0) # '(20 - 5)' -> '15.0'
        = 3.0 * (0.6 + 15.0) # '(9 / 3)' -> '3.0'
        = 3.0 * 15.6 # '(0.6 + 15.0)' -> '15.6'
        = 46.8 # '3.0 * 15.6' -> '46.8'




        Extensions:




        1. Write your program such that it can parse Polish notation without brackets. That's what Polish notation is invented for.

        2. Handle edge cases such as ((* 3 15)) (Polish).

        3. Write test suites to ensure that the expressions are evaluated correctly. While addition and multiplication is commutative, subtraction and division are not, and the order of the numbers matter.

        4. Follow suggestions in l0b0's answer, which are cosmetic. I did not make any cosmetic suggestions as I thought that it was more important to settle the logic of the program first.

        5. Have your program create an abstract syntax tree instead of doing string operations (such as replacing innermost expression with its value).


        Feel free to ask questions in the comments.






        share|improve this answer











        $endgroup$
















          5












          5








          5





          $begingroup$

          Good job on adding the test suites; they make testing code extremely easy.



          Recursion often results in a clean solution. However, I think that this problem can be solved just as well with an iterative approach.



          The key to innermost_parens is to return the text between the last ( and the first ). Recursion is a viable solution, but I find that this for loop does the job better and in a cleaner way:



          def innermost_parens(text):
          """
          Returns the innermost parenthesis.

          >>> innermost_parens('* 1 2')
          '* 1 2'
          >>> innermost_parens("1 + (2 * (4 - 1))")
          '(4 - 1)'
          >>> innermost_parens("1 + (2 * (4 * (2 + (8 * 7)) - 1))")
          '(8 * 7)'
          >>> innermost_parens("(1 + 2) * (3 + 4)")
          '(1 + 2)'
          """
          start = 0
          end = len(text)
          for (i, c) in enumerate(text):
          if c == '(':
          start = i
          if c == ')':
          end = i + 1
          break
          return text[start:end]


          Take note that this function will return the first innermost expression with brackets. The reason for this will be clear later.



          As for polish_eval, your program doesn't follow the standard Polish notation. In the standard Polish notation you can only perform operations on two numbers. It is also unclear what 'start' does. I modified your code to do the above and added tests for subtraction and division:



          def polish_eval(expr):
          """
          Reduced Polish notation.

          >>> polish_eval('+ 4 1')
          5.0
          >>> polish_eval('* 4 5')
          20.0
          >>> polish_eval('- 20 3')
          17.0
          >>> polish_eval('/ 20 5')
          4.0
          """
          oper, a, b = expr.split()
          return OP_FOR_SYMBOL[oper](float(a), float(b))


          I'll leave the documentation of the Polish notation to you. No changes were made to infix_eval.



          Finally, we're on to full_eval. Since there's been a major change in the notation we support, we'll have to rewrite the code. First, to start off with the tests:



          >>> full_eval("* (* 4 5) (+ 4 1)", polish_eval)
          100.0
          >>> full_eval("* 4 (/ 1 10)", polish_eval)
          0.4
          >>> full_eval("1 + (5 * 2)", infix_eval)
          11.0


          Rewriting Polish notation without brackets would require the use of a stack. This will not fit nicely in your full_eval function, so I will not implement the notation without brackets. In this function, the use of recursion is unnecessary and a while loop will fit the problem nicely.



          def full_eval(expr, eval_type):
          """
          Evals by the rules of eval_type starting from the inner
          parenthesis.

          >>> full_eval("* (* 4 5) (+ 4 1)", polish_eval)
          100.0
          >>> full_eval("* 4 (/ 1 10)", polish_eval)
          0.4
          >>> full_eval("1 + (5 * 2)", infix_eval)
          11.0
          """
          while len(expr.split()) != 1:
          inn = innermost_parens(expr)
          result = eval_type(inn.strip('(').strip(')').strip())
          expr = expr.replace(inn, str(result))
          return float(expr)


          Before I evaluate inn, I first strip off the brackets which enclose the expression, as well as any remaining whitespace.



          The reason why I wrote innermost_parens as shown above is to allow for the easy replacement of the expression. Here's a trace of an expression in Polish:



            * (/ 9 3) (+ (/ 3 5) (- 20 5))
          = * (/ 9 3) (+ 0.6 (- 20 5)) # '(/ 3 5)' -> '0.6'
          = * (/ 9 3) (+ 0.6 15.0) # '(- 20 5)' -> '15.0'
          = * 3.0 (+ 0.6 15.0) # '(/ 9 3)' -> '3.0'
          = * 3.0 15.6 # '(+ 0.6 15.0)' -> '15.6'
          = 46.8 # '* 3.0 15.6' -> '46.8'


          and the same expression in infix:



            (9 / 3) * ((3 / 5) + (20 - 5))
          = (9 / 3) * (0.6 + (20 - 5)) # '(3 / 5)' -> '0.6'
          = (9 / 3) * (0.6 + 15.0) # '(20 - 5)' -> '15.0'
          = 3.0 * (0.6 + 15.0) # '(9 / 3)' -> '3.0'
          = 3.0 * 15.6 # '(0.6 + 15.0)' -> '15.6'
          = 46.8 # '3.0 * 15.6' -> '46.8'




          Extensions:




          1. Write your program such that it can parse Polish notation without brackets. That's what Polish notation is invented for.

          2. Handle edge cases such as ((* 3 15)) (Polish).

          3. Write test suites to ensure that the expressions are evaluated correctly. While addition and multiplication is commutative, subtraction and division are not, and the order of the numbers matter.

          4. Follow suggestions in l0b0's answer, which are cosmetic. I did not make any cosmetic suggestions as I thought that it was more important to settle the logic of the program first.

          5. Have your program create an abstract syntax tree instead of doing string operations (such as replacing innermost expression with its value).


          Feel free to ask questions in the comments.






          share|improve this answer











          $endgroup$



          Good job on adding the test suites; they make testing code extremely easy.



          Recursion often results in a clean solution. However, I think that this problem can be solved just as well with an iterative approach.



          The key to innermost_parens is to return the text between the last ( and the first ). Recursion is a viable solution, but I find that this for loop does the job better and in a cleaner way:



          def innermost_parens(text):
          """
          Returns the innermost parenthesis.

          >>> innermost_parens('* 1 2')
          '* 1 2'
          >>> innermost_parens("1 + (2 * (4 - 1))")
          '(4 - 1)'
          >>> innermost_parens("1 + (2 * (4 * (2 + (8 * 7)) - 1))")
          '(8 * 7)'
          >>> innermost_parens("(1 + 2) * (3 + 4)")
          '(1 + 2)'
          """
          start = 0
          end = len(text)
          for (i, c) in enumerate(text):
          if c == '(':
          start = i
          if c == ')':
          end = i + 1
          break
          return text[start:end]


          Take note that this function will return the first innermost expression with brackets. The reason for this will be clear later.



          As for polish_eval, your program doesn't follow the standard Polish notation. In the standard Polish notation you can only perform operations on two numbers. It is also unclear what 'start' does. I modified your code to do the above and added tests for subtraction and division:



          def polish_eval(expr):
          """
          Reduced Polish notation.

          >>> polish_eval('+ 4 1')
          5.0
          >>> polish_eval('* 4 5')
          20.0
          >>> polish_eval('- 20 3')
          17.0
          >>> polish_eval('/ 20 5')
          4.0
          """
          oper, a, b = expr.split()
          return OP_FOR_SYMBOL[oper](float(a), float(b))


          I'll leave the documentation of the Polish notation to you. No changes were made to infix_eval.



          Finally, we're on to full_eval. Since there's been a major change in the notation we support, we'll have to rewrite the code. First, to start off with the tests:



          >>> full_eval("* (* 4 5) (+ 4 1)", polish_eval)
          100.0
          >>> full_eval("* 4 (/ 1 10)", polish_eval)
          0.4
          >>> full_eval("1 + (5 * 2)", infix_eval)
          11.0


          Rewriting Polish notation without brackets would require the use of a stack. This will not fit nicely in your full_eval function, so I will not implement the notation without brackets. In this function, the use of recursion is unnecessary and a while loop will fit the problem nicely.



          def full_eval(expr, eval_type):
          """
          Evals by the rules of eval_type starting from the inner
          parenthesis.

          >>> full_eval("* (* 4 5) (+ 4 1)", polish_eval)
          100.0
          >>> full_eval("* 4 (/ 1 10)", polish_eval)
          0.4
          >>> full_eval("1 + (5 * 2)", infix_eval)
          11.0
          """
          while len(expr.split()) != 1:
          inn = innermost_parens(expr)
          result = eval_type(inn.strip('(').strip(')').strip())
          expr = expr.replace(inn, str(result))
          return float(expr)


          Before I evaluate inn, I first strip off the brackets which enclose the expression, as well as any remaining whitespace.



          The reason why I wrote innermost_parens as shown above is to allow for the easy replacement of the expression. Here's a trace of an expression in Polish:



            * (/ 9 3) (+ (/ 3 5) (- 20 5))
          = * (/ 9 3) (+ 0.6 (- 20 5)) # '(/ 3 5)' -> '0.6'
          = * (/ 9 3) (+ 0.6 15.0) # '(- 20 5)' -> '15.0'
          = * 3.0 (+ 0.6 15.0) # '(/ 9 3)' -> '3.0'
          = * 3.0 15.6 # '(+ 0.6 15.0)' -> '15.6'
          = 46.8 # '* 3.0 15.6' -> '46.8'


          and the same expression in infix:



            (9 / 3) * ((3 / 5) + (20 - 5))
          = (9 / 3) * (0.6 + (20 - 5)) # '(3 / 5)' -> '0.6'
          = (9 / 3) * (0.6 + 15.0) # '(20 - 5)' -> '15.0'
          = 3.0 * (0.6 + 15.0) # '(9 / 3)' -> '3.0'
          = 3.0 * 15.6 # '(0.6 + 15.0)' -> '15.6'
          = 46.8 # '3.0 * 15.6' -> '46.8'




          Extensions:




          1. Write your program such that it can parse Polish notation without brackets. That's what Polish notation is invented for.

          2. Handle edge cases such as ((* 3 15)) (Polish).

          3. Write test suites to ensure that the expressions are evaluated correctly. While addition and multiplication is commutative, subtraction and division are not, and the order of the numbers matter.

          4. Follow suggestions in l0b0's answer, which are cosmetic. I did not make any cosmetic suggestions as I thought that it was more important to settle the logic of the program first.

          5. Have your program create an abstract syntax tree instead of doing string operations (such as replacing innermost expression with its value).


          Feel free to ask questions in the comments.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Apr 13 '17 at 12:40









          Community

          1




          1










          answered Apr 9 '15 at 13:41









          wei2912wei2912

          893523




          893523

























              2












              $begingroup$

              Some suggestions:




              • Rather than ask whether all the following expressions are going to be in Polish or infix notation, you can simply check whether the first non-parenthesis character is a number or an operator.


              • START_FOR_SYMBOLS could be renamed to OPERATION_IDENTITIES for mathematical clarity.


              Open issues:




              • Why doctest.testmod() on every run?

              • There isn't always a unique innermost parenthesis, for example in the case of (1 + 2) * (3 + 4). You probably want to return a list and process each of them in turn.






              share|improve this answer









              $endgroup$


















                2












                $begingroup$

                Some suggestions:




                • Rather than ask whether all the following expressions are going to be in Polish or infix notation, you can simply check whether the first non-parenthesis character is a number or an operator.


                • START_FOR_SYMBOLS could be renamed to OPERATION_IDENTITIES for mathematical clarity.


                Open issues:




                • Why doctest.testmod() on every run?

                • There isn't always a unique innermost parenthesis, for example in the case of (1 + 2) * (3 + 4). You probably want to return a list and process each of them in turn.






                share|improve this answer









                $endgroup$
















                  2












                  2








                  2





                  $begingroup$

                  Some suggestions:




                  • Rather than ask whether all the following expressions are going to be in Polish or infix notation, you can simply check whether the first non-parenthesis character is a number or an operator.


                  • START_FOR_SYMBOLS could be renamed to OPERATION_IDENTITIES for mathematical clarity.


                  Open issues:




                  • Why doctest.testmod() on every run?

                  • There isn't always a unique innermost parenthesis, for example in the case of (1 + 2) * (3 + 4). You probably want to return a list and process each of them in turn.






                  share|improve this answer









                  $endgroup$



                  Some suggestions:




                  • Rather than ask whether all the following expressions are going to be in Polish or infix notation, you can simply check whether the first non-parenthesis character is a number or an operator.


                  • START_FOR_SYMBOLS could be renamed to OPERATION_IDENTITIES for mathematical clarity.


                  Open issues:




                  • Why doctest.testmod() on every run?

                  • There isn't always a unique innermost parenthesis, for example in the case of (1 + 2) * (3 + 4). You probably want to return a list and process each of them in turn.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Apr 9 '15 at 13:22









                  l0b0l0b0

                  4,6181023




                  4,6181023






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Code Review Stack Exchange!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      Use MathJax to format equations. MathJax reference.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f86389%2frecursive-math-expression-eval%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      is 'sed' thread safeWhat should someone know about using Python scripts in the shell?Nexenta bash script uses...

                      How do i solve the “ No module named 'mlxtend' ” issue on Jupyter?

                      Pilgersdorf Inhaltsverzeichnis Geografie | Geschichte | Bevölkerungsentwicklung | Politik | Kultur...