Compare commits

...

2 Commits

Author SHA1 Message Date
Arijit De
b4521ea28f Fixes #8754, #8724 Updated postfix_evaluation.py
postfix_evaluation.py now supports Unary operators and floating point numbers.
Also merged evaluate_postfix_notations.py and postfix_evaluation.py into postfix_evaluation.py which fixes #8724. Added a doctest example with unary operator.
2023-08-04 21:27:20 +05:30
Arijit De
d6874ad24f Made the requested changes
Also changed the code to make the evaluate function first convert all the numbers and then process the valid expression.
2023-08-04 21:18:39 +05:30

View File

@ -24,8 +24,14 @@ Enter a Postfix Equation (space separated) = 5 6 9 * +
Result = 59 Result = 59
""" """
# Defining valid unary operator symbols
UNARY_OP_SYMBOLS = ("-", "+")
def get_number(data: str) -> tuple[bool, int] | tuple[bool, float] | tuple[bool, str]: # Defining valid binary operator symbols
BINARY_OP_SYMBOLS = ("-", "+", "*", "^", "/")
def get_number(data: str) -> int | float | str:
""" """
Converts the given data to appropriate number if it is indeed a number, else returns Converts the given data to appropriate number if it is indeed a number, else returns
the data as it is with a False flag. This function also serves as a check of whether the data as it is with a False flag. This function also serves as a check of whether
@ -43,20 +49,19 @@ def get_number(data: str) -> tuple[bool, int] | tuple[bool, float] | tuple[bool,
or numeric) and 'b' is either an integer of a floating point number. or numeric) and 'b' is either an integer of a floating point number.
If 'a' is False, then b is 'data' If 'a' is False, then b is 'data'
""" """
int_val = 0
float_val = 0.0
try: try:
int_val = int(data) return int(data)
return True, int_val
except ValueError: except ValueError:
try: try:
float_val = float(data) return float(data)
return True, float_val
except ValueError: except ValueError:
return False, data if is_operator(data):
return data
msg = f"{data} is neither a number nor a valid operator"
raise ValueError(msg)
def is_operator(data: str) -> bool: def is_operator(data: str | int | float) -> bool:
""" """
Checks whether a given input is one of the valid operators or not. Checks whether a given input is one of the valid operators or not.
Valid operators being '-', '+', '*', '^' and '/'. Valid operators being '-', '+', '*', '^' and '/'.
@ -71,10 +76,10 @@ def is_operator(data: str) -> bool:
bool bool
True if data is an operator else False. True if data is an operator else False.
""" """
return data in ["-", "+", "*", "^", "/"] return data in BINARY_OP_SYMBOLS
def evaluate(post_fix: list, verbose: bool = False) -> int | float | str | None: def evaluate(post_fix: list[str], verbose: bool = False) -> int | float | str | None:
""" """
Function that evaluates postfix expression using a stack. Function that evaluates postfix expression using a stack.
>>> evaluate(["2", "1", "+", "3", "*"]) >>> evaluate(["2", "1", "+", "3", "*"])
@ -83,6 +88,8 @@ def evaluate(post_fix: list, verbose: bool = False) -> int | float | str | None:
6.6 6.6
>>> evaluate(["2", "-", "3", "+"]) >>> evaluate(["2", "-", "3", "+"])
1 1
>>> evaluate(["-4", "5", "*", "6", "-"])
-26
>>> evaluate([]) >>> evaluate([])
0 0
@ -100,7 +107,9 @@ def evaluate(post_fix: list, verbose: bool = False) -> int | float | str | None:
int int
The evaluated value The evaluated value
""" """
x: str | int | float
stack = [] stack = []
valid_expression = []
opr = { opr = {
"^": lambda p, q: p**q, "^": lambda p, q: p**q,
"*": lambda p, q: p * q, "*": lambda p, q: p * q,
@ -108,14 +117,17 @@ def evaluate(post_fix: list, verbose: bool = False) -> int | float | str | None:
"+": lambda p, q: p + q, "+": lambda p, q: p + q,
"-": lambda p, q: p - q, "-": lambda p, q: p - q,
} # operators & their respective operation } # operators & their respective operation
if len(post_fix) > 0: if len(post_fix) == 0:
return 0
# Checking the list to find out whether the postfix expression is valid
for x in post_fix:
valid_expression.append(get_number(x))
if verbose: if verbose:
# print table header # print table header
print("Symbol".center(8), "Action".center(12), "Stack", sep=" | ") print("Symbol".center(8), "Action".center(12), "Stack", sep=" | ")
print("-" * (30 + len(post_fix))) print("-" * (30 + len(post_fix)))
for x in post_fix: for x in valid_expression:
is_number, x = get_number(x) if not is_operator(x):
if is_number: # if x is a number (integer, float)
stack.append(x) # append x to stack stack.append(x) # append x to stack
if verbose: if verbose:
# output in tabular format # output in tabular format
@ -125,14 +137,14 @@ def evaluate(post_fix: list, verbose: bool = False) -> int | float | str | None:
stack, stack,
sep=" | ", sep=" | ",
) )
elif is_operator(x): continue
# If x is operator
# If only 1 value is inside stack and + or - is encountered # If only 1 value is inside stack and + or - is encountered
# then this is unary + or - case # then this is unary + or - case
if x in ["-", "+"] and len(stack) < 2: if x in UNARY_OP_SYMBOLS and len(stack) < 2:
b = stack.pop() # pop stack b = stack.pop() # pop stack
if x == "-": if x == "-":
stack.append(-b) # negate b and push again into stack b *= -1 # negate b
else: # when x is unary +
stack.append(b) stack.append(b)
if verbose: if verbose:
# output in tabular format # output in tabular format
@ -148,7 +160,7 @@ def evaluate(post_fix: list, verbose: bool = False) -> int | float | str | None:
stack, stack,
sep=" | ", sep=" | ",
) )
else: continue
b = stack.pop() # pop stack b = stack.pop() # pop stack
if verbose: if verbose:
# output in tabular format # output in tabular format
@ -169,7 +181,7 @@ def evaluate(post_fix: list, verbose: bool = False) -> int | float | str | None:
sep=" | ", sep=" | ",
) )
# evaluate the 2 values popped from stack & push result to stack # evaluate the 2 values popped from stack & push result to stack
stack.append(opr[x](a, b)) stack.append(opr[str(x)](a, b))
if verbose: if verbose:
# output in tabular format # output in tabular format
print( print(
@ -178,14 +190,14 @@ def evaluate(post_fix: list, verbose: bool = False) -> int | float | str | None:
stack, stack,
sep=" | ", sep=" | ",
) )
else: # else:
print(f"{x} is neither a number, nor a valid operator") # msg = f"{x} is neither a number nor a valid operator"
break # raise ValueError(msg)
# If everything executed correctly,the stack will contain # If everything executed correctly, the stack will contain
# only one element which is the result # only one element which is the result
result = stack[0] if len(stack) == 1 else None if len(stack) != 1:
return result raise ArithmeticError("Input is not a valid postfix expression")
return 0 return stack[0]
def is_yes(val: str) -> bool: def is_yes(val: str) -> bool:
@ -203,18 +215,20 @@ def is_yes(val: str) -> bool:
bool bool
True if Yes, otherwise False True if Yes, otherwise False
""" """
return val in ["Y", "y"] return val in ("Y", "y")
if __name__ == "__main__": if __name__ == "__main__":
loop = True loop = True
# Creating a loop so that user can evaluate postfix expression multiple times # Creating a loop so that user can evaluate postfix expression multiple times
while loop: while True:
expression = input("Enter a Postfix Expression (space separated): ").split(" ") expression = input("Enter a Postfix Expression (space separated): ").split(" ")
choice = input("Do you want to see stack contents while evaluating? [y/N]: ") choice = input(
"Do you want to see stack contents while evaluating? [y/N]: "
).strip()
display = is_yes(choice) display = is_yes(choice)
output = evaluate(expression, display) output = evaluate(expression, display)
if output is not None:
print("Result = ", output) print("Result = ", output)
choice = input("Do you want to enter another expression? [y/N]: ") choice = input("Do you want to enter another expression? [y/N]: ")
loop = is_yes(choice) if not is_yes(choice):
break