mirror of
https://hub.njuu.cf/TheAlgorithms/Python.git
synced 2023-10-11 13:06:12 +08:00
Updated postfix_evaluation.py to support Unary operators (#8787)
* Updated postfix_evaluation.py to support Unary operators and floating point numbers Fixes #8754 and #8724 Also merged evaluate_postfix_notations.py and postfix_evaluation.py into postfix_evaluation.py Signed-off-by: Arijit De <arijitde2050@gmail.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated postfix_evaluation.py to support Unary operators and floating point numbers. Fixes #8754 and formatted code to pass ruff and black test. Also merged evaluate_postfix_notations.py and postfix_evaluation.py into postfix_evaluation.py which fixes #8724 and made sure it passes doctest Signed-off-by: Arijit De <arijitde2050@gmail.com> * Fixed return type hinting required by pre commit for evaluate function Signed-off-by: Arijit De <arijitde2050@gmail.com> * Changed line 186 to return only top of stack instead of calling the get_number function as it was converting float values to int, resulting in data loss. Fixes #8754 and #8724 Signed-off-by: Arijit De <arijitde2050@gmail.com> * Made the requested changes Also changed the code to make the evaluate function first convert all the numbers and then process the valid expression. * 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. * 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. * Fixes #8754, #8724 Updated the parse_token function of postfix_evaluation.py ostfix_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 and invalid expression. * 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 and invalid expression. * Update postfix_evaluation.py * Update postfix_evaluation.py * Update postfix_evaluation.py * Update postfix_evaluation.py * Update postfix_evaluation.py --------- Signed-off-by: Arijit De <arijitde2050@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss <cclauss@me.com>
This commit is contained in:
parent
fceacf977f
commit
0a9438071e
@ -1,52 +0,0 @@
|
|||||||
"""
|
|
||||||
The Reverse Polish Nation also known as Polish postfix notation
|
|
||||||
or simply postfix notation.
|
|
||||||
https://en.wikipedia.org/wiki/Reverse_Polish_notation
|
|
||||||
Classic examples of simple stack implementations
|
|
||||||
Valid operators are +, -, *, /.
|
|
||||||
Each operand may be an integer or another expression.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
|
|
||||||
def evaluate_postfix(postfix_notation: list) -> int:
|
|
||||||
"""
|
|
||||||
>>> evaluate_postfix(["2", "1", "+", "3", "*"])
|
|
||||||
9
|
|
||||||
>>> evaluate_postfix(["4", "13", "5", "/", "+"])
|
|
||||||
6
|
|
||||||
>>> evaluate_postfix([])
|
|
||||||
0
|
|
||||||
"""
|
|
||||||
if not postfix_notation:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
operations = {"+", "-", "*", "/"}
|
|
||||||
stack: list[Any] = []
|
|
||||||
|
|
||||||
for token in postfix_notation:
|
|
||||||
if token in operations:
|
|
||||||
b, a = stack.pop(), stack.pop()
|
|
||||||
if token == "+":
|
|
||||||
stack.append(a + b)
|
|
||||||
elif token == "-":
|
|
||||||
stack.append(a - b)
|
|
||||||
elif token == "*":
|
|
||||||
stack.append(a * b)
|
|
||||||
else:
|
|
||||||
if a * b < 0 and a % b != 0:
|
|
||||||
stack.append(a // b + 1)
|
|
||||||
else:
|
|
||||||
stack.append(a // b)
|
|
||||||
else:
|
|
||||||
stack.append(int(token))
|
|
||||||
|
|
||||||
return stack.pop()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import doctest
|
|
||||||
|
|
||||||
doctest.testmod()
|
|
@ -1,4 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
|
Reverse Polish Nation is also known as Polish postfix notation or simply postfix
|
||||||
|
notation.
|
||||||
|
https://en.wikipedia.org/wiki/Reverse_Polish_notation
|
||||||
|
Classic examples of simple stack implementations.
|
||||||
|
Valid operators are +, -, *, /.
|
||||||
|
Each operand may be an integer or another expression.
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
Enter a Postfix Equation (space separated) = 5 6 9 * +
|
Enter a Postfix Equation (space separated) = 5 6 9 * +
|
||||||
@ -17,52 +24,177 @@ Enter a Postfix Equation (space separated) = 5 6 9 * +
|
|||||||
Result = 59
|
Result = 59
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import operator as op
|
# Defining valid unary operator symbols
|
||||||
|
UNARY_OP_SYMBOLS = ("-", "+")
|
||||||
|
|
||||||
|
# operators & their respective operation
|
||||||
|
OPERATORS = {
|
||||||
|
"^": lambda p, q: p**q,
|
||||||
|
"*": lambda p, q: p * q,
|
||||||
|
"/": lambda p, q: p / q,
|
||||||
|
"+": lambda p, q: p + q,
|
||||||
|
"-": lambda p, q: p - q,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def solve(post_fix):
|
def parse_token(token: str | float) -> float | str:
|
||||||
stack = []
|
"""
|
||||||
div = lambda x, y: int(x / y) # noqa: E731 integer division operation
|
Converts the given data to the appropriate number if it is indeed a number, else
|
||||||
opr = {
|
returns the data as it is with a False flag. This function also serves as a check
|
||||||
"^": op.pow,
|
of whether the input is a number or not.
|
||||||
"*": op.mul,
|
|
||||||
"/": div,
|
|
||||||
"+": op.add,
|
|
||||||
"-": op.sub,
|
|
||||||
} # operators & their respective operation
|
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
token: The data that needs to be converted to the appropriate operator or number.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
float or str
|
||||||
|
Returns a float if `token` is a number or a str if `token` is an operator
|
||||||
|
"""
|
||||||
|
if token in OPERATORS:
|
||||||
|
return token
|
||||||
|
try:
|
||||||
|
return float(token)
|
||||||
|
except ValueError:
|
||||||
|
msg = f"{token} is neither a number nor a valid operator"
|
||||||
|
raise ValueError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def evaluate(post_fix: list[str], verbose: bool = False) -> float:
|
||||||
|
"""
|
||||||
|
Evaluate postfix expression using a stack.
|
||||||
|
>>> evaluate(["0"])
|
||||||
|
0.0
|
||||||
|
>>> evaluate(["-0"])
|
||||||
|
-0.0
|
||||||
|
>>> evaluate(["1"])
|
||||||
|
1.0
|
||||||
|
>>> evaluate(["-1"])
|
||||||
|
-1.0
|
||||||
|
>>> evaluate(["-1.1"])
|
||||||
|
-1.1
|
||||||
|
>>> evaluate(["2", "1", "+", "3", "*"])
|
||||||
|
9.0
|
||||||
|
>>> evaluate(["2", "1.9", "+", "3", "*"])
|
||||||
|
11.7
|
||||||
|
>>> evaluate(["2", "-1.9", "+", "3", "*"])
|
||||||
|
0.30000000000000027
|
||||||
|
>>> evaluate(["4", "13", "5", "/", "+"])
|
||||||
|
6.6
|
||||||
|
>>> evaluate(["2", "-", "3", "+"])
|
||||||
|
1.0
|
||||||
|
>>> evaluate(["-4", "5", "*", "6", "-"])
|
||||||
|
-26.0
|
||||||
|
>>> evaluate([])
|
||||||
|
0
|
||||||
|
>>> evaluate(["4", "-", "6", "7", "/", "9", "8"])
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ArithmeticError: Input is not a valid postfix expression
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
post_fix:
|
||||||
|
The postfix expression is tokenized into operators and operands and stored
|
||||||
|
as a Python list
|
||||||
|
|
||||||
|
verbose:
|
||||||
|
Display stack contents while evaluating the expression if verbose is True
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
float
|
||||||
|
The evaluated value
|
||||||
|
"""
|
||||||
|
if not post_fix:
|
||||||
|
return 0
|
||||||
|
# Checking the list to find out whether the postfix expression is valid
|
||||||
|
valid_expression = [parse_token(token) for token in post_fix]
|
||||||
|
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)))
|
||||||
|
stack = []
|
||||||
for x in post_fix:
|
for x in valid_expression:
|
||||||
if x.isdigit(): # if x in digit
|
if x not in OPERATORS:
|
||||||
stack.append(x) # append x to stack
|
stack.append(x) # append x to stack
|
||||||
# output in tabular format
|
if verbose:
|
||||||
print(x.rjust(8), ("push(" + x + ")").ljust(12), ",".join(stack), sep=" | ")
|
|
||||||
else:
|
|
||||||
b = stack.pop() # pop stack
|
|
||||||
# output in tabular format
|
|
||||||
print("".rjust(8), ("pop(" + b + ")").ljust(12), ",".join(stack), sep=" | ")
|
|
||||||
|
|
||||||
a = stack.pop() # pop stack
|
|
||||||
# output in tabular format
|
|
||||||
print("".rjust(8), ("pop(" + a + ")").ljust(12), ",".join(stack), sep=" | ")
|
|
||||||
|
|
||||||
stack.append(
|
|
||||||
str(opr[x](int(a), int(b)))
|
|
||||||
) # evaluate the 2 values popped from stack & push result to stack
|
|
||||||
# output in tabular format
|
# output in tabular format
|
||||||
print(
|
print(
|
||||||
x.rjust(8),
|
f"{x}".rjust(8),
|
||||||
("push(" + a + x + b + ")").ljust(12),
|
f"push({x})".ljust(12),
|
||||||
",".join(stack),
|
stack,
|
||||||
|
sep=" | ",
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
# If x is operator
|
||||||
|
# If only 1 value is inside the stack and + or - is encountered
|
||||||
|
# then this is unary + or - case
|
||||||
|
if x in UNARY_OP_SYMBOLS and len(stack) < 2:
|
||||||
|
b = stack.pop() # pop stack
|
||||||
|
if x == "-":
|
||||||
|
b *= -1 # negate b
|
||||||
|
stack.append(b)
|
||||||
|
if verbose:
|
||||||
|
# output in tabular format
|
||||||
|
print(
|
||||||
|
"".rjust(8),
|
||||||
|
f"pop({b})".ljust(12),
|
||||||
|
stack,
|
||||||
|
sep=" | ",
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
str(x).rjust(8),
|
||||||
|
f"push({x}{b})".ljust(12),
|
||||||
|
stack,
|
||||||
|
sep=" | ",
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
b = stack.pop() # pop stack
|
||||||
|
if verbose:
|
||||||
|
# output in tabular format
|
||||||
|
print(
|
||||||
|
"".rjust(8),
|
||||||
|
f"pop({b})".ljust(12),
|
||||||
|
stack,
|
||||||
sep=" | ",
|
sep=" | ",
|
||||||
)
|
)
|
||||||
|
|
||||||
return int(stack[0])
|
a = stack.pop() # pop stack
|
||||||
|
if verbose:
|
||||||
|
# output in tabular format
|
||||||
|
print(
|
||||||
|
"".rjust(8),
|
||||||
|
f"pop({a})".ljust(12),
|
||||||
|
stack,
|
||||||
|
sep=" | ",
|
||||||
|
)
|
||||||
|
# evaluate the 2 values popped from stack & push result to stack
|
||||||
|
stack.append(OPERATORS[x](a, b)) # type: ignore[index]
|
||||||
|
if verbose:
|
||||||
|
# output in tabular format
|
||||||
|
print(
|
||||||
|
f"{x}".rjust(8),
|
||||||
|
f"push({a}{x}{b})".ljust(12),
|
||||||
|
stack,
|
||||||
|
sep=" | ",
|
||||||
|
)
|
||||||
|
# If everything is executed correctly, the stack will contain
|
||||||
|
# only one element which is the result
|
||||||
|
if len(stack) != 1:
|
||||||
|
raise ArithmeticError("Input is not a valid postfix expression")
|
||||||
|
return float(stack[0])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
Postfix = input("\n\nEnter a Postfix Equation (space separated) = ").split(" ")
|
# Create a loop so that the user can evaluate postfix expressions multiple times
|
||||||
print("\n\tResult = ", solve(Postfix))
|
while True:
|
||||||
|
expression = input("Enter a Postfix Expression (space separated): ").split(" ")
|
||||||
|
prompt = "Do you want to see stack contents while evaluating? [y/N]: "
|
||||||
|
verbose = input(prompt).strip().lower() == "y"
|
||||||
|
output = evaluate(expression, verbose)
|
||||||
|
print("Result = ", output)
|
||||||
|
prompt = "Do you want to enter another expression? [y/N]: "
|
||||||
|
if input(prompt).strip().lower() != "y":
|
||||||
|
break
|
||||||
|
Loading…
Reference in New Issue
Block a user