0

I need to make a program that takes user input (expr) of math expression and treats finds the answer. however, we are not allowed to use the eval() function. the assignment states that the only operators we should assume the user will input are: +, -, *, /, %. operands are also assumed to be one-digit integers.

my thinking was to convert operands to integers and make a list of all the operators that could be used. then, use an if statement to see how the operator matches my list.

so far I was able to come up with these lines of code:

operand1 = expr[1]
operator = expr[2]
operand2 = expr[3]

operators = ['+','-','*','/','%']

I did this to index the location of each operand and the operator in the inputted expression. I am stuck here and was hoping someone could give me some help as to how to move forward. the result of the code needs to output the expression entered by the user, as well as the result of the expression. if the second operand of the expression is a 0 and the operator is division, the code outputs 'None'.

wewewe132
  • 11
  • 2
  • ast.literal_eval can evaluate literal expressions, what do those look like? – cs95 Jul 14 '20 at 18:55
  • the typical approach for this problem is to convert to postfix notation (or just to a postfix stack), and then resolve it. – njzk2 Jul 14 '20 at 18:55
  • Look into syntax trees for mathmatical expressions. You may also be interested in the `operator` module. – Paul M. Jul 14 '20 at 18:56
  • https://stackoverflow.com/questions/38860682/evaluating-a-mathematical-expression-without-eval-on-python3 – liorsanders Jul 14 '20 at 18:59
  • Would something like the Parse Tree [here](https://runestone.academy/runestone/books/published/pythonds/Trees/ParseTree.html) work? – Philippe Dixon Jul 14 '20 at 19:02
  • And keep in mind that indexing starts at 0. So the very first character inside `expr` should be accessed via `expr[0]`. What might also help is the following post: https://stackoverflow.com/a/40097699 – Daniel B. Jul 14 '20 at 19:12

2 Answers2

2

You can parse the expression with ast.parse.

>>> import ast
>>> expr = ast.parse("3 + 5", mode="eval")

Then you can analyze the resulting parse tree. In this particular instance, you care about the body of the expression.

>>> expr
<_ast.Expression object at 0x10992c438>
>>> expr.body
<_ast.BinOp object at 0x10a926320>

This object has there attributes of interest: left, op, and right. You'll look at the operator

>>> expr.body.op
<_ast.Add object at 0x10a91a208>

to decide how to handle the operands.

>>> expr.body.left.n + expr.body.right.n
8

So a simple recursive function that can handle multiplication and addition might look like

def evaluate_expr(expr):
    if isinstance(expr, ast.Expression):
        return evaluate_expr(expr.body)
    elif isinstance(expr, ast.Num):
        return expr.n
    elif isinstance(expr, ast.BinOp):
        op = expr.op
        left = evaluate_expr(expr.left)
        right = evaluate_expr(expr.right)
        if isinstance(op, ast.Add):
            return left + right
        elif isinstance(op, ast.Mult):
            return left * right
    raise ValueError(f"Can't evaluate {expr}")

e = ast.parse("3 + 5 * 2", mode="eval")
print(evaluate_expr(e.body))  # Outputs 13

See the documentation for the ast module to learn what other nodes can appear in the tree, so you can adapt evaluate_expr to handle other operation, parentheses, etc. ast.dump is also useful to explore how the expression is parsed.

>>> ast.dump(e, annotate_fields=False)
'Expression(BinOp(Num(3), Add(), BinOp(Num(5), Mult(), Num(2))))'

This shows clearly that the parser handles precedence: 3 + 5 * 2 is not 3 + 5 with the result multiplied by 2, but rather 3 plus the result of 5 * 2 (lower nodes have higher precedence, since the tree is evaluated from the bottom up).


This assumes that your input is, in fact, a valid Python expression. If not, you would need to write your own parser, but once you have a parse tree, the evaluation of that tree proceeds in a similar fashion (though the nodes of the tree are whatever you create in your parse, not necessarily the ast.Bin et al. nodes created by ast.parse).

chepner
  • 497,756
  • 71
  • 530
  • 681
0

If you only have one operator in this input-string(example 5 + 9)then you first make a list with all of the operators you want to calculate operators = ["+", "-", "*", "/", "%"]

Then you have to go through every operator in this list and if its in the string, go ahead

# I'm defining the string here manually but you can do it with input()
expression_string = "5 + 9"

for i in range(0, len(operators)):
    if operators[i] in expression_string:
        ....

If the operator is in that expression_string, first you have to split it into its two operands and convert both of them in floats

operands = expression_string.split(operators[i])

# Converting the two numbers into floats
operands[0] = float(operands[0])
operands[1] = float(operands[1])

Now the final step is to go through every operator you want to calculate with an if-loop and calculate it.

if operators[i] == "+":
    result = operands[0] + operands[1]

elif operators[i] == "-":
    result = operands[0] - operands[1]

elif operators[i] == "*":
    result = operands[0] * operands[1]

elif operators[i] == "/":
    result = operands[0] / operands[1]

elif operators[i] == "%":
    result = operands[0] % operands[1]

Now you have the result of this string stored in the variable "result"

I hope this could help you

EDIT: you can simply write print(result) under the last if-loop to print your result. Also you can check in the loop elif operators[i] == "/" if the operand is an 0 and then save result as None

Builditluc
  • 353
  • 1
  • 7