Skip to content

Commit 0a94380

Browse files
arijitde92pre-commit-ci[bot]cclauss
authored
Updated postfix_evaluation.py to support Unary operators (TheAlgorithms#8787)
* Updated postfix_evaluation.py to support Unary operators and floating point numbers Fixes TheAlgorithms#8754 and TheAlgorithms#8724 Also merged evaluate_postfix_notations.py and postfix_evaluation.py into postfix_evaluation.py Signed-off-by: Arijit De <[email protected]> * [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 TheAlgorithms#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 TheAlgorithms#8724 and made sure it passes doctest Signed-off-by: Arijit De <[email protected]> * Fixed return type hinting required by pre commit for evaluate function Signed-off-by: Arijit De <[email protected]> * 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 TheAlgorithms#8754 and TheAlgorithms#8724 Signed-off-by: Arijit De <[email protected]> * 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 TheAlgorithms#8754, TheAlgorithms#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 TheAlgorithms#8724. Added a doctest example with unary operator. * Fixes TheAlgorithms#8754, TheAlgorithms#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 TheAlgorithms#8724. Added a doctest example with unary operator. * Fixes TheAlgorithms#8754, TheAlgorithms#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 TheAlgorithms#8724. Added a doctest example with unary operator and invalid expression. * Fixes TheAlgorithms#8754, TheAlgorithms#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 TheAlgorithms#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 <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss <[email protected]>
1 parent fceacf9 commit 0a94380

File tree

2 files changed

+166
-86
lines changed

2 files changed

+166
-86
lines changed

data_structures/stacks/evaluate_postfix_notations.py

-52
This file was deleted.
+166-34
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
"""
2+
Reverse Polish Nation is also known as Polish postfix notation or simply postfix
3+
notation.
4+
https://en.wikipedia.org/wiki/Reverse_Polish_notation
5+
Classic examples of simple stack implementations.
6+
Valid operators are +, -, *, /.
7+
Each operand may be an integer or another expression.
8+
29
Output:
310
411
Enter a Postfix Equation (space separated) = 5 6 9 * +
@@ -17,52 +24,177 @@
1724
Result = 59
1825
"""
1926

20-
import operator as op
27+
# Defining valid unary operator symbols
28+
UNARY_OP_SYMBOLS = ("-", "+")
29+
30+
# operators & their respective operation
31+
OPERATORS = {
32+
"^": lambda p, q: p**q,
33+
"*": lambda p, q: p * q,
34+
"/": lambda p, q: p / q,
35+
"+": lambda p, q: p + q,
36+
"-": lambda p, q: p - q,
37+
}
38+
39+
40+
def parse_token(token: str | float) -> float | str:
41+
"""
42+
Converts the given data to the appropriate number if it is indeed a number, else
43+
returns the data as it is with a False flag. This function also serves as a check
44+
of whether the input is a number or not.
45+
46+
Parameters
47+
----------
48+
token: The data that needs to be converted to the appropriate operator or number.
49+
50+
Returns
51+
-------
52+
float or str
53+
Returns a float if `token` is a number or a str if `token` is an operator
54+
"""
55+
if token in OPERATORS:
56+
return token
57+
try:
58+
return float(token)
59+
except ValueError:
60+
msg = f"{token} is neither a number nor a valid operator"
61+
raise ValueError(msg)
62+
63+
64+
def evaluate(post_fix: list[str], verbose: bool = False) -> float:
65+
"""
66+
Evaluate postfix expression using a stack.
67+
>>> evaluate(["0"])
68+
0.0
69+
>>> evaluate(["-0"])
70+
-0.0
71+
>>> evaluate(["1"])
72+
1.0
73+
>>> evaluate(["-1"])
74+
-1.0
75+
>>> evaluate(["-1.1"])
76+
-1.1
77+
>>> evaluate(["2", "1", "+", "3", "*"])
78+
9.0
79+
>>> evaluate(["2", "1.9", "+", "3", "*"])
80+
11.7
81+
>>> evaluate(["2", "-1.9", "+", "3", "*"])
82+
0.30000000000000027
83+
>>> evaluate(["4", "13", "5", "/", "+"])
84+
6.6
85+
>>> evaluate(["2", "-", "3", "+"])
86+
1.0
87+
>>> evaluate(["-4", "5", "*", "6", "-"])
88+
-26.0
89+
>>> evaluate([])
90+
0
91+
>>> evaluate(["4", "-", "6", "7", "/", "9", "8"])
92+
Traceback (most recent call last):
93+
...
94+
ArithmeticError: Input is not a valid postfix expression
95+
96+
Parameters
97+
----------
98+
post_fix:
99+
The postfix expression is tokenized into operators and operands and stored
100+
as a Python list
21101
102+
verbose:
103+
Display stack contents while evaluating the expression if verbose is True
22104
23-
def solve(post_fix):
105+
Returns
106+
-------
107+
float
108+
The evaluated value
109+
"""
110+
if not post_fix:
111+
return 0
112+
# Checking the list to find out whether the postfix expression is valid
113+
valid_expression = [parse_token(token) for token in post_fix]
114+
if verbose:
115+
# print table header
116+
print("Symbol".center(8), "Action".center(12), "Stack", sep=" | ")
117+
print("-" * (30 + len(post_fix)))
24118
stack = []
25-
div = lambda x, y: int(x / y) # noqa: E731 integer division operation
26-
opr = {
27-
"^": op.pow,
28-
"*": op.mul,
29-
"/": div,
30-
"+": op.add,
31-
"-": op.sub,
32-
} # operators & their respective operation
33-
34-
# print table header
35-
print("Symbol".center(8), "Action".center(12), "Stack", sep=" | ")
36-
print("-" * (30 + len(post_fix)))
37-
38-
for x in post_fix:
39-
if x.isdigit(): # if x in digit
119+
for x in valid_expression:
120+
if x not in OPERATORS:
40121
stack.append(x) # append x to stack
41-
# output in tabular format
42-
print(x.rjust(8), ("push(" + x + ")").ljust(12), ",".join(stack), sep=" | ")
43-
else:
122+
if verbose:
123+
# output in tabular format
124+
print(
125+
f"{x}".rjust(8),
126+
f"push({x})".ljust(12),
127+
stack,
128+
sep=" | ",
129+
)
130+
continue
131+
# If x is operator
132+
# If only 1 value is inside the stack and + or - is encountered
133+
# then this is unary + or - case
134+
if x in UNARY_OP_SYMBOLS and len(stack) < 2:
44135
b = stack.pop() # pop stack
136+
if x == "-":
137+
b *= -1 # negate b
138+
stack.append(b)
139+
if verbose:
140+
# output in tabular format
141+
print(
142+
"".rjust(8),
143+
f"pop({b})".ljust(12),
144+
stack,
145+
sep=" | ",
146+
)
147+
print(
148+
str(x).rjust(8),
149+
f"push({x}{b})".ljust(12),
150+
stack,
151+
sep=" | ",
152+
)
153+
continue
154+
b = stack.pop() # pop stack
155+
if verbose:
45156
# output in tabular format
46-
print("".rjust(8), ("pop(" + b + ")").ljust(12), ",".join(stack), sep=" | ")
157+
print(
158+
"".rjust(8),
159+
f"pop({b})".ljust(12),
160+
stack,
161+
sep=" | ",
162+
)
47163

48-
a = stack.pop() # pop stack
164+
a = stack.pop() # pop stack
165+
if verbose:
49166
# output in tabular format
50-
print("".rjust(8), ("pop(" + a + ")").ljust(12), ",".join(stack), sep=" | ")
51-
52-
stack.append(
53-
str(opr[x](int(a), int(b)))
54-
) # evaluate the 2 values popped from stack & push result to stack
167+
print(
168+
"".rjust(8),
169+
f"pop({a})".ljust(12),
170+
stack,
171+
sep=" | ",
172+
)
173+
# evaluate the 2 values popped from stack & push result to stack
174+
stack.append(OPERATORS[x](a, b)) # type: ignore[index]
175+
if verbose:
55176
# output in tabular format
56177
print(
57-
x.rjust(8),
58-
("push(" + a + x + b + ")").ljust(12),
59-
",".join(stack),
178+
f"{x}".rjust(8),
179+
f"push({a}{x}{b})".ljust(12),
180+
stack,
60181
sep=" | ",
61182
)
62-
63-
return int(stack[0])
183+
# If everything is executed correctly, the stack will contain
184+
# only one element which is the result
185+
if len(stack) != 1:
186+
raise ArithmeticError("Input is not a valid postfix expression")
187+
return float(stack[0])
64188

65189

66190
if __name__ == "__main__":
67-
Postfix = input("\n\nEnter a Postfix Equation (space separated) = ").split(" ")
68-
print("\n\tResult = ", solve(Postfix))
191+
# Create a loop so that the user can evaluate postfix expressions multiple times
192+
while True:
193+
expression = input("Enter a Postfix Expression (space separated): ").split(" ")
194+
prompt = "Do you want to see stack contents while evaluating? [y/N]: "
195+
verbose = input(prompt).strip().lower() == "y"
196+
output = evaluate(expression, verbose)
197+
print("Result = ", output)
198+
prompt = "Do you want to enter another expression? [y/N]: "
199+
if input(prompt).strip().lower() != "y":
200+
break

0 commit comments

Comments
 (0)