Compare commits

...

8 Commits

Author SHA1 Message Date
Christian Clauss
51df05ce11
Update postfix_evaluation.py 2023-08-23 14:30:48 +02:00
Christian Clauss
0b1627a12d
Update postfix_evaluation.py 2023-08-23 14:25:18 +02:00
Christian Clauss
d3f4ee8bbd
Update postfix_evaluation.py 2023-08-23 13:42:32 +02:00
Christian Clauss
b0c44a6856
Update postfix_evaluation.py 2023-08-23 13:39:52 +02:00
Christian Clauss
b2bb0afd5e
Update postfix_evaluation.py 2023-08-23 13:37:39 +02:00
Arijit De
c11b18fed8 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.
2023-08-23 16:33:01 +05:30
Arijit De
9a15148f8b 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.
2023-08-23 16:01:23 +05:30
Arijit De
b4f51a6ac3 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-23 13:25:30 +05:30

View File

@ -1,8 +1,8 @@
"""
The Reverse Polish Nation also known as Polish postfix notation
or simply postfix notation.
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
Classic examples of simple stack implementations.
Valid operators are +, -, *, /.
Each operand may be an integer or another expression.
@ -27,119 +27,109 @@ Enter a Postfix Equation (space separated) = 5 6 9 * +
# Defining valid unary operator symbols
UNARY_OP_SYMBOLS = ("-", "+")
# 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
the data as it is with a False flag. This function also serves as a check of whether
the input is a number or not.
Parameters
----------
data : str
The data which needs to be converted to the appropriate number
Returns
-------
bool, int or float
Returns a tuple of (a, b) where 'a' is True if data is indeed a number (integer
or numeric) and 'b' is either an integer of a floating point number.
If 'a' is False, then b is 'data'
"""
try:
return int(data)
except ValueError:
try:
return float(data)
except ValueError:
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 | int | float) -> bool:
"""
Checks whether a given input is one of the valid operators or not.
Valid operators being '-', '+', '*', '^' and '/'.
Parameters
----------
data : str
The value that needs to be checked for operator
Returns
-------
bool
True if data is an operator else False.
"""
return data in BINARY_OP_SYMBOLS
def evaluate(post_fix: list[str], verbose: bool = False) -> int | float | str | None:
"""
Function that evaluates postfix expression using a stack.
>>> evaluate(["2", "1", "+", "3", "*"])
9
>>> evaluate(["4", "13", "5", "/", "+"])
6.6
>>> evaluate(["2", "-", "3", "+"])
1
>>> evaluate(["-4", "5", "*", "6", "-"])
-26
>>> evaluate([])
0
Parameters
----------
post_fix : list
The postfix expression tokenized into operators and operands and stored as a
python list
verbose : bool
Display stack contents while evaluating the expression if verbose is True
Returns
-------
int
The evaluated value
"""
x: str | int | float
stack = []
valid_expression = []
opr = {
# 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,
} # operators & their respective operation
if len(post_fix) == 0:
}
def parse_token(token: str | float) -> float | str:
"""
Converts the given data to the 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 input is a number or not.
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
for x in post_fix:
valid_expression.append(get_number(x))
valid_expression = [parse_token(token) for token in post_fix]
if verbose:
# print table header
print("Symbol".center(8), "Action".center(12), "Stack", sep=" | ")
print("-" * (30 + len(post_fix)))
stack = []
for x in valid_expression:
if not is_operator(x):
if x not in OPERATORS:
stack.append(x) # append x to stack
if verbose:
# output in tabular format
print(
str(x).rjust(8),
("push(" + str(x) + ")").ljust(12),
f"{x}".rjust(8),
f"push({x})".ljust(12),
stack,
sep=" | ",
)
continue
# If x is operator
# If only 1 value is inside stack and + or - is encountered
# 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
@ -150,13 +140,13 @@ def evaluate(post_fix: list[str], verbose: bool = False) -> int | float | str |
# output in tabular format
print(
"".rjust(8),
("pop(" + str(b) + ")").ljust(12),
f"pop({b})".ljust(12),
stack,
sep=" | ",
)
print(
str(x).rjust(8),
("push(" + str(x) + str(b) + ")").ljust(12),
f"push({x}{b})".ljust(12),
stack,
sep=" | ",
)
@ -166,7 +156,7 @@ def evaluate(post_fix: list[str], verbose: bool = False) -> int | float | str |
# output in tabular format
print(
"".rjust(8),
("pop(" + str(b) + ")").ljust(12),
f"pop({b})".ljust(12),
stack,
sep=" | ",
)
@ -176,59 +166,35 @@ def evaluate(post_fix: list[str], verbose: bool = False) -> int | float | str |
# output in tabular format
print(
"".rjust(8),
("pop(" + str(a) + ")").ljust(12),
f"pop({a})".ljust(12),
stack,
sep=" | ",
)
# evaluate the 2 values popped from stack & push result to stack
stack.append(opr[str(x)](a, b))
stack.append(OPERATORS[x](a, b)) # type: ignore[index]
if verbose:
# output in tabular format
print(
str(x).rjust(8),
("push(" + str(a) + str(x) + str(b) + ")").ljust(12),
f"{x}".rjust(8),
f"push({a}{x}{b})".ljust(12),
stack,
sep=" | ",
)
# else:
# msg = f"{x} is neither a number nor a valid operator"
# raise ValueError(msg)
# If everything executed correctly, the stack will contain
# 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 stack[0]
def is_yes(val: str) -> bool:
"""
Function that checks whether a user has entered any representation of a Yes (y, Y).
Any other input is considered as a No.
Parameters
-----------
val : str
The value entered by user
Returns
-------
bool
True if Yes, otherwise False
"""
return val in ("Y", "y")
return float(stack[0])
if __name__ == "__main__":
loop = True
# Creating a loop so that user can evaluate postfix expression multiple times
# Create a loop so that the user can evaluate postfix expressions multiple times
while True:
expression = input("Enter a Postfix Expression (space separated): ").split(" ")
choice = input(
"Do you want to see stack contents while evaluating? [y/N]: "
).strip()
display = is_yes(choice)
output = evaluate(expression, display)
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)
choice = input("Do you want to enter another expression? [y/N]: ")
if not is_yes(choice):
prompt = "Do you want to enter another expression? [y/N]: "
if input(prompt).strip().lower() != "y":
break