I have code to generate a postfix expression from infix and generate an expression tree from the postfix notation. The problem is, if I give it an expression like 45 / 15 * 3, it will give me the result 1 instead of 9, because it is solving the deepest levels of the tree first. Is there another traversal I can do to evaluate the expression properly?
def evaluate(self):
if not self.is_empty():
if not infix_to_postfix.is_operator(self._root):
return float(self._root)
else:
A = self._left.evaluate()
B = self._right.evaluate()
if self._root == "+":
return A + B
elif self._root == "-":
return A - B
elif self._root == "*":
return A * B
elif self._root == "/":
return A / B
def InfixToPostfix(exp: str):
exp = exp.replace(" ", "")
S = Stack()
postfix = ""
j = 0
for i in range(len(exp)):
if is_operand(exp[i]):
if i + 1 <= len(exp) - 1 and is_operand(exp[i+1]):
continue
else:
j = i
while j - 1 >= 0 and is_operand(exp[j - 1]):
if is_operand(exp[j]):
j -= 1
else:
break
postfix += exp[j:i + 1] + " "
elif is_operator(exp[i]):
while not S.is_empty() and S.top() != "(" and \ HasHigherPrecedence(S.top(), exp[i]):
if is_operator(S.top()):
postfix += S.top() + " "
else:
postfix += S.top()
S.pop()
S.push(exp[i])
elif exp[i] == "(":
S.push(exp[i])
elif exp[i] == ")":
while not S.is_empty() and S.top() != "(":
if is_operator(S.top()):
postfix += S.top() + " "
else:
postfix += S.top()
S.pop()
else:
print("There's an invalid character")
return
while not S.is_empty():
if S.top() == '(':
S.pop()
continue
if is_operator(S.top()):
postfix += S.top() + " "
else:
postfix += S.top()
S.pop()
return postfix
def HasHigherPrecedence(op1: str, op2: str):
op1_weight = get_operator_weight(op1)
op2_weight = get_operator_weight(op2)
return op1_weight > op2_weight
The postfix expression of your example 45 / 15 * 3 would be:
45 15 / 3 *
So the tree generated would look like:
*
/ 3
45 15
So your traversal algorithm appears correct, as it would do 45 / 15 = 3, then 3 * 3 = 9.
The issue is actually pretty minor in your postfix generator. Specifically, in the function HasHigherPrecedence, you should return op1_weight >= op2_weight. It should be greater than or equal to because in examples such as this one where the operators have the same precedence, they should be executed in the order they appear. So division would be done first.
Related
I have been looking at my code for awhile and need to return a polynomial given a dictionary. The polynomial output should not have any 1's as coefficients. Her e is my code:
class Polynomial:
# Constructor
def __init__(self, polyDict):
self.polyDict = polyDict
# String Method
def __str__(self):
polyStr = ""
firstTerm = True
for exp, coeff in sorted(self.polyDict.items(), reverse=True):
if coeff == 0:
continue
if firstTerm:
if coeff > 0:
polyStr += str(coeff)
else:
polyStr += str(coeff)
if exp > 1:
polyStr += "x^" + str(exp)
elif exp == 1:
polyStr += "x"
firstTerm = False
else:
if coeff > 0:
polyStr += "+" + str(coeff)
else:
polyStr += str(coeff)
if exp > 1:
polyStr += "x^" + str(exp)
elif exp == 1:
polyStr += "x"
return polyStr
print(Polynomial({6:-3, 5:6, 4:-1, 3:-2, 2:0, 1:1, 0:-3}))
I am unsure what else to try. I've staired at the code for a couple hours and dont know what to add.
I think this combines as much as can be combined.
class Polynomial:
# Constructor
def __init__(self, polyDict):
self.polyDict = polyDict
# String Method
def __str__(self):
polyStr = ""
firstTerm = True
for exp, coeff in sorted(self.polyDict.items(), reverse=True):
if coeff == 0:
continue
if coeff < -1 or (firstTerm and coeff > 1):
polyStr += str(coeff)
elif coeff == -1:
polyStr += '-'
elif coeff == 1:
if not firstTerm:
polyStr += '+'
else:
polyStr += '+'+str(coeff)
firstTerm = False
if exp > 1:
polyStr += "x^"+str(exp)
elif exp == 1:
polyStr += "x"
return polyStr
print(Polynomial({6:-3, 5:6, 4:-1, 3:-2, 2:0, 1:1, 0:-3}))
I am trying to make a simple calculator with an input of the whole equation, but the catch is that i can not use eval() or anything similar to it. I wrote something and is probably not the best solution but this is what i came up with. The problem is that if i enter "2 + 5" as an input the final output is an error saying that it can not int() "2+"
Here is the code:
print("Calculator 2.0")
while True:
equation = input(": ")
# Remove spaces
calculate = equation.replace(" ", "")
# Converting to list
calculate = list(calculate)
# Merging numbers next to one another
i = 0
startingIndex = -1
numCounter = 1
num = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
while i < len(calculate):
if calculate[i] in num:
if startingIndex == -1:
startingIndex = i
numCounter += 1
else:
calculate[startingIndex : numCounter] = [''.join(calculate[startingIndex : numCounter])]
startingIndex = -1
numCounter = 1
i += 1
solved = False
answer = 0
while solved == False:
# Check for multiplication and division
i = 0
while i < len(calculate):
divideIndex = -1
multiplyIndex = -1
j = 0
while j < len(calculate):
if calculate[j] == "*":
multiplyIndex = j
elif calculate[j] == "/":
divideIndex = j
j += 1
# Solve the multiplication and division
if multiplyIndex != -1:
calculate[multiplyIndex] = str(int(calculate[multiplyIndex - 1]) * int(calculate[multiplyIndex + 1]))
del calculate[multiplyIndex - 1]
del calculate[multiplyIndex + 1]
if divideIndex != -1:
calculate[divideIndex] = str(int(calculate[divideIndex - 1] / int(calculate[divideIndex + 1])))
del calculate[divideIndex - 1]
del calculate[divideIndex + 1]
i += 1
# Check for addition and subtraction
i = 0
while i < len(calculate):
sumIndex = -1
subtractIndex = -1
j = 0
while j < len(calculate):
if calculate[j] == "+":
sumIndex = j
elif calculate[j] == "-":
subtractIndex = j
j += 1
# Solve the addition and subtraction
if sumIndex != -1:
calculate[sumIndex] = str(int(calculate[sumIndex - 1]) + int(calculate[sumIndex + 1]))
del calculate[sumIndex - 1]
del calculate[sumIndex + 1]
if subtractIndex != -1:
calculate[subtractIndex] = str(int(calculate[subtractIndex - 1]) - int(calculate[subtractIndex + 1]))
del calculate[subtractIndex - 1]
del calculate[subtractIndex + 1]
i += 1
answer = int(calculate[0])
print(answer)
solved = True
Give this a shot:
def Numbers(var):
return (
var == "0"
or var == "1"
or var == "2"
or var == "3"
or var == "4"
or var == "5"
or var == "6"
or var == "7"
or var == "8"
or var == "9"
)
def Test4Num(varstr):
n = 0
var = ""
try:
while Numbers(varstr[n]):
var += varstr[n]
n += 1
except:
pass
return (int(var), n)
def operation(string, num1, num2):
if string == "+":
return num1 + num2
if string == "-":
return num1 - num2
if string == "*":
return num1 * num2
if string == "/":
return num1 / num2
if string == "^":
return num1**num2
def operator(operato):
return (
operato == "+"
or operato == "-"
or operato == "*"
or operato == "/"
or operato == "^"
)
def eval_math_expr(expr):
negate = False
expr = expr.replace(" ", "")
while True:
try:
if expr[0] == "-": # for negative numbers
negate = True # because here the numbers are string format
expr = expr[1:]
number1 = Test4Num(expr)[0]
if negate == True:
number1 = -number1
negate = False
end_number1 = Test4Num(expr)[1]
expr = expr[end_number1:]
if expr == "":
return number1
op = expr[0]
expr = expr[1:]
number2 = Test4Num(expr)[0]
end_number2 = Test4Num(expr)[1]
result = operation(op, number1, number2)
number1 = result
expr = str(number1) + expr[end_number2:]
except Exception as e:
print(e)
break
return number1
if __name__ == "__main__":
expr = input("Enter your expression:")
print(expr + "=")
print(eval_math_expr(expr))
i'm making expression parsing algorithm using stack by python and when i input equation without whitespaces like this "1+2*3+10" it doesn't work properly,sometimes give error result and sometimes it give an error , it should be entered like "1 + 2 * 3 + 10" to work
def applyOp(a, b, op):
if op == '+': return a + b
if op == '-': return a - b
if op == '*': return a * b
if op == '/': return a // b
def evaluate(tokens):
values = Stack()
ops = Stack()
i = 0
while i < len(tokens):
if tokens[i] == ' ':
i += 1
continue
elif tokens[i] == '(':
ops.push(tokens[i])
elif tokens[i].isdigit():
val = 0
while (i < len(tokens) and
tokens[i].isdigit()):
val = (val * 10) + int(tokens[i])
i += 1
values.push(val)
elif tokens[i] == ')':
while ops.peek() != None and ops.peek() != '(':
val2 = values.pop()
val1 = values.pop()
op = ops.pop()
# print(val2 , val1 , op)
values.push(applyOp(val1, val2, op))
ops.pop()
else:
while (ops.peek() != None and
precedence(ops.peek()) >= precedence(tokens[i])):
val2 = values.pop()
val1 = values.pop()
op = ops.pop()
values.push(applyOp(val1, val2, op))
ops.push(tokens[i])
i += 1
while ops.peek() != None:
val2 = values.pop()
val1 = values.pop()
op = ops.pop()
values.push(applyOp(val1, val2, op))
return values.peek()
I'm not sure if this is the only problem, but
elif tokens[i].isdigit():
val = 0
while (i < len(tokens) and
tokens[i].isdigit()):
val = (val * 10) + int(tokens[i])
i += 1
values.push(val)
advances i too far. It stops when i is already pointing to something that's not a digit, and then when you advance i again at the end of the outer while loop you will miss whatever was there, which doesn't matter if it's a space - so that's why spaces fix it.
EDIT: Okay so I tried it out, swapping the mentioned elif statement with this should work:
elif tokens[i].isdigit():
val = int(tokens[i])
while i+1 < len(tokens) and tokens[i+1].isdigit():
i += 1
val = (val * 10) + int(tokens[i])
values.append(val)
I am making a program which converting Prefix to Infix and I have a problem with putting brackets to expression.
For converting, I use classic stack algorithm. Here is my function for brackits.
Input:
//+86 16 67/*/31 53 85 15
My output:
((86+16)/67)/(((31/53)*85)/15)
Expecting output :
(86+16)/67/(31/53*85/15)
def breckets(operand1, operand2, operace):
res = ""
if (operand1.isdigit()) and (operand2.isdigit()):
res = operand1 + operace + operand2
elif (operand1.isdigit()) and (not operand2.isdigit()):
if operace == "+":
res = operand1 + operace + operand2
else:
res = operand1 + operace + "(" + operand2 + ")"
elif (not operand1.isdigit()) and (operand2.isdigit()):
if prior(operace) != 0:
res = "(" + operand1 + ")" + operace + operand2
else:
res = operand1 + operace + operand2
else:
res = "(" + operand1 + ")" + operace + "(" + operand2 + ")"
return res
def prior(a):
prior = None
if a in "+-":
prior = 0
elif a in "*/":
prior = 1
elif a in "^":
prior = 2
else:
print("Something went wrong")
exit()
return prior
But I must not use parentheses unnecessarily, can anybody advice me something please?
The code below can get what you expect. but I am not sure if the algorithm is right as I am not familiar with the data structure.
I use a basic function to sort the list several times, instead of sorting the list strictly from right to left, you can change some lines to break the while loop to make it so if that is what you want.
Anyway hope this can help you somehow.
operators = "+-*/^"
# presume the input is a finely spaced str that can be splitted into a proper list
# ipt = "/ * + 86 16 67 / * / + 31 53 85 15 / / 33 45 74" # for testing
ipt = "/ / + 86 16 67 / * / 31 53 85 15"
ip = ipt.split()
def sorting(lst):
res = []
i = len(lst)-1 # check list items from right to left
while i >= 0:
if i >= 3:
if lst[i-2] in operators:
# check if the operator is followed by two numbers
if lst[i-1] not in operators and lst[i] not in operators:
# check if the operator is + | -, the result should be in parentheses
if lst[i-2] in "+-":
res.append("(" + lst[i-1] + lst[i-2] + lst[i] + ")")
i -=3
continue
# if the operator is following another operator, the result shouldn't be in parentheses
if lst[i-3] in operators:
res.append(lst[i-1] + lst[i-2] + lst[i])
i -=3
continue
# if the operator is following a number, the result shouldn be in parentheses
else:
res.append("(" + lst[i-1] + lst[i-2] + lst[i] + ")")
i -= 3
continue
# this is to check the first item of the list is an operator and followed by two numbers
elif i == 2:
if lst[i-2] in operators:
if lst[i-1] not in operators and lst[i] not in operators:
res.append(lst[i-1] + lst[i-2] + lst[i])
i -=3
continue
res.append(lst[i])
i -= 1
# as all items are appending to the new list, so the positions are totally reversed
return list(reversed(res))
def no_more_operators(lst):
# to check if the lst is sortable
# one scenario is there are exccesive numbers, but no operators
# the current function can only check if any operators is in the list
for op in operators:
if op in lst:
return False
return True
def no_more_numbers(lst):
# to check if the lst is sortable
# one scenario is there are exccesive operators, but no numbers
# the current function can only check if any number is in the list
for i in lst:
if i.isnumeric():
return False
return True
# keep sorting the list until there is no more numbers or n0 more operators
while not no_more_numbers(ip) or not no_more_operators(ip):
ip = sorting(ip)
print(ip)
output:
['/', '/', '(86+16)', '67', '/', '*', '31/53', '85', '15']
['/', '(86+16)/67', '/', '31/53*85', '15']
['/', '(86+16)/67', '(31/53*85/15)']
['(86+16)/67/(31/53*85/15)']
I am trying to write an Infix to Prefix Converter where e.g. I would like to convert this:
1 + ((C + A ) * (B - F))
to something like:
add(1, multiply(add(C, A), subtract(B, F)))
but I get this instead :
multiply(add(1, add(C, A), subtract(B, F)))
This is the code I have so far
postfix = []
temp = []
newTemp = []
def textOperator(s):
if s is '+':
return 'add('
elif s is '-':
return 'subtract('
elif s is '*':
return 'multiply('
else:
return ""
def typeof(s):
if s is '(':
return leftparentheses
elif s is ')':
return rightparentheses
elif s is '+' or s is '-' or s is '*' or s is '%' or s is '/':
return operator
elif s is ' ':
return empty
else :
return operand
infix = "1 + ((C + A ) * (B - F))"
for i in infix :
type = typeof(i)
if type is operand:
newTemp.append(i)
elif type is operator:
postfix.append(textOperator(i))
postfix.append(newTemp.pop())
postfix.append(', ')
elif type is leftparentheses :
newTemp.append(i)
elif type is rightparentheses :
next = newTemp.pop()
while next is not '(':
postfix.append(next)
next = newTemp.pop()
postfix.append(')')
newTemp.append(''.join(postfix))
while len(postfix) > 0 :
postfix.pop()
elif type is empty:
continue
print("newTemp = ", newTemp)
print("postfix = ", postfix)
while len(newTemp) > 0 :
postfix.append(newTemp.pop())
postfix.append(')')
print(''.join(postfix))
Can someone please help me figure out how I would fix this.
What I see, with the parenthetical clauses, is a recursive problem crying out for a recursive solution. The following is a rethink of your program that might give you some ideas of how to restructure it, even if you don't buy into my recursion argument:
import sys
from enum import Enum
class Type(Enum): # This could also be done with individual classes
leftparentheses = 0
rightparentheses = 1
operator = 2
empty = 3
operand = 4
OPERATORS = { # get your data out of your code...
"+": "add",
"-": "subtract",
"*": "multiply",
"%": "modulus",
"/": "divide",
}
def textOperator(string):
if string not in OPERATORS:
sys.exit("Unknown operator: " + string)
return OPERATORS[string]
def typeof(string):
if string == '(':
return Type.leftparentheses
elif string == ')':
return Type.rightparentheses
elif string in OPERATORS:
return Type.operator
elif string == ' ':
return Type.empty
else:
return Type.operand
def process(tokens):
stack = []
while tokens:
token = tokens.pop()
category = typeof(token)
print("token = ", token, " (" + str(category) + ")")
if category == Type.operand:
stack.append(token)
elif category == Type.operator:
stack.append((textOperator(token), stack.pop(), process(tokens)))
elif category == Type.leftparentheses:
stack.append(process(tokens))
elif category == Type.rightparentheses:
return stack.pop()
elif category == Type.empty:
continue
print("stack = ", stack)
return stack.pop()
INFIX = "1 + ((C + A ) * (B - F))"
# pop/append work from right, so reverse, and require a real list
postfix = process(list(INFIX[::-1]))
print(postfix)
The result of this program is a structure like:
('add', '1', ('multiply', ('add', 'C', 'A'), ('subtract', 'B', 'F')))
Which you should be able to post process into the string form you desire (again, recursively...)
PS: type and next are Python built-ins and/or reserved words, don't use them for variable names.
PPS: replace INFIX[::-1] with sys.argv[1][::-1] and you can pass test cases into the program to see what it does with them.
PPPS: like your original, this only handles single digit numbers (or single letter variables), you'll need to provide a better tokenizer than list() to get that working right.