Skip to content

Commit b164ada

Browse files
committed
String Calculate
1 parent 5dbf768 commit b164ada

File tree

1 file changed

+272
-0
lines changed

1 file changed

+272
-0
lines changed

src/medium/StringCalculate.java

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
package medium;
2+
3+
import java.util.ArrayList;
4+
import java.util.Dictionary;
5+
import java.util.Hashtable;
6+
import java.util.Stack;
7+
import java.util.regex.Matcher;
8+
import java.util.regex.Pattern;
9+
10+
/**
11+
* Have the function StringCalculate(str)
12+
* take the str parameter being passed and evaluate
13+
* the mathematical expression within in.
14+
* The double asterisks (**) represent exponentiation.
15+
* ---
16+
* For example, if str were "(2+(3-1)*3)**3" the output should be 512.
17+
* Another example: if str is "(2-0)(6/2)" the output should be 6.
18+
* There can be parenthesis within the string, so you must evaluate
19+
* it properly according to the rules of arithmetic.
20+
* The string will contain the operators: +, -, /, *, (, ), and **.
21+
* If you have a string like this: #/#*# or #+#(#)/#, then evaluate from left to right.
22+
* So divide then multiply, and for the second one multiply, divide, then add.
23+
* The evaluations will be such that there will not be any decimal operations,
24+
* so you do not need to account for rounding.
25+
*/
26+
public class StringCalculate {
27+
28+
// evaluate the Postfix expression
29+
private static final Stack<String> posStack = new Stack<>();
30+
31+
/**
32+
* Cleaning, parsing and splitting the input string into the array of tokens.
33+
*
34+
* @param str input string
35+
* @return String array with parsed operators and operands
36+
*/
37+
private static String[] parseConsoleInput(String str) {
38+
// 1. convert to lowercase
39+
// 2. replace exponent symbol ** with ^
40+
// 3. replace (a)(b) with (a) * (b)
41+
// 4. replace a(b) with a * (b)
42+
// 5. split two bundled characters (but only when the second character is not a digit)
43+
// 6. split bundled command/operator and a digit. Excl. minus to keep negative numbers intact.
44+
// 7. remove multiple spaces
45+
// 8. trim (remove leading and trailing spaces)
46+
// 9. split to array of strings
47+
return str
48+
.toLowerCase()
49+
.replaceAll("(\\*\\*)", "^")
50+
.replaceAll("(\\)\\()", ")*(")
51+
.replaceAll("([0-9])(?=[(])", "$1 *")
52+
.replaceAll("([\\p{P}\\p{S}a-z0-9])(?=[\\p{P}\\p{S}a-z])", "$1 ")
53+
.replaceAll("([^0-9])(?=[0-9])", "$1 ")
54+
.replaceAll(" +", " ")
55+
.trim().split(" ");
56+
}
57+
58+
/**
59+
* Prints out a message to the console if the user input is invalid.
60+
*
61+
* @param op single element of the input string
62+
*/
63+
private static void printInputError(String op) {
64+
System.out.println("Unrecognised operator or operand: \"" + op + "\".");
65+
}
66+
67+
/**
68+
* Reduces two operands to a single result
69+
* by performing an arithmetical operation.
70+
*
71+
* @param a operand A
72+
* @param b operand B
73+
* @param operator denotes operation type (addition, substraction, division etc.)
74+
* @return result of the arithmetical operation
75+
* @throws ArithmeticException if divisor is 0
76+
*/
77+
public static long reduceOperands(long a, long b, String operator) {
78+
switch (operator) {
79+
case "+":
80+
return a + b;
81+
case "-":
82+
return a - b;
83+
case "*":
84+
return a * b;
85+
case "/":
86+
if (b == 0) {
87+
System.out.println("Divide by 0.");
88+
throw new ArithmeticException();
89+
}
90+
return a / b;
91+
case "^":
92+
return (long) Math.pow(a, b);
93+
default:
94+
return 0;
95+
}
96+
}
97+
98+
/**
99+
* Checks if the token is an operand (0-9).
100+
*
101+
* @param op a single token from the input string
102+
* @return true if the token is an operand, false if not
103+
*/
104+
private static boolean isOperand(String op) {
105+
Pattern pattern = Pattern.compile("^[\\d]|^-[\\d]");
106+
Matcher matcher = pattern.matcher(op);
107+
return matcher.find();
108+
}
109+
110+
/**
111+
* Checks if the token is an operator + - * / : ^ ( ) etc.
112+
*
113+
* @param op a single token from the input string
114+
* @return true if the token is an operator, false if not
115+
*/
116+
private static boolean isOperator(String op) {
117+
Pattern pattern = Pattern.compile("^[+\\-*/^%]");
118+
Matcher matcher = pattern.matcher(op);
119+
return matcher.find();
120+
}
121+
122+
/**
123+
* Converts the Infix expression to Postfix.
124+
*
125+
* @param tokens expression tokens that are already parsed and split
126+
* @return a postfix expression
127+
*/
128+
private static String[] convertToPostfix(String[] tokens) {
129+
Stack<String> infStack = new Stack<>();
130+
String terminating = "#";
131+
Dictionary<String, Integer> precedence = new Hashtable<>() {
132+
{
133+
put(terminating, 0);
134+
put("(", 0);
135+
put(")", 0);
136+
put("+", 1);
137+
put("-", 1);
138+
put("*", 2);
139+
put("/", 2);
140+
put("^", 3);
141+
}
142+
};
143+
ArrayList<String> output = new ArrayList<>();
144+
infStack.push(terminating);
145+
for (String token : tokens) {
146+
// token is an operand, add to output and move on
147+
if (isOperand(token)) {
148+
output.add(token);
149+
continue;
150+
}
151+
// left parenthesis, push it to stack and move on
152+
if (token.equals("(")) {
153+
infStack.push(token);
154+
continue;
155+
}
156+
// right parenthesis, keep popping until the left parenthesis is found
157+
if (token.equals(")")) {
158+
while (true) {
159+
String op = infStack.pop();
160+
if (op.equals("(")) {
161+
break;
162+
} else {
163+
output.add(op);
164+
}
165+
}
166+
continue;
167+
}
168+
// token is an operator
169+
if (isOperator(token)) {
170+
int cmp1 = precedence.get(token);
171+
while (true) {
172+
int cmp2 = precedence.get(infStack.peek());
173+
// operand has higher precedence than item on the top of stack
174+
if (cmp1 > cmp2) {
175+
infStack.push(token);
176+
break;
177+
} else {
178+
output.add(infStack.pop());
179+
}
180+
}
181+
}
182+
}
183+
// pop the remaining items until the terminating symbol is reached (complete)
184+
while (!infStack.empty() && !infStack.peek().equals(terminating)) {
185+
output.add(infStack.pop());
186+
}
187+
return output.toArray(new String[0]);
188+
}
189+
190+
/**
191+
* Takes two operands from stack and perform the operation with a provider operator.
192+
*
193+
* @param operator denotes operation type (addition, substraction, division etc.)
194+
* @return result of the evaluation
195+
*/
196+
private static String performArithOperation(String operator) {
197+
if (posStack.size() >= 2) {
198+
// Safe to evaluate
199+
String elementB = posStack.pop();
200+
String elementA = posStack.pop();
201+
long opB = Long.parseLong(elementB);
202+
long opA = Long.parseLong(elementA);
203+
long result = reduceOperands(opA, opB, operator);
204+
return Long.toString(result);
205+
} else {
206+
// Stack underflow since at least one element is null
207+
return null;
208+
}
209+
}
210+
211+
/**
212+
* Computes the entire expression in Reverse Polish Notation.
213+
*
214+
* @param tokens expression tokens that are already parsed and split to Array of Strings
215+
* @return result of the evaluation
216+
*/
217+
private static Long evaluateExpression(String[] tokens) {
218+
for (String token : tokens) {
219+
// token is an operand, push it to stack and move on
220+
if (isOperand(token)) {
221+
posStack.push(token);
222+
continue;
223+
}
224+
// token is an operator, evaluate
225+
if (isOperator(token)) {
226+
String result = performArithOperation(token);
227+
if (result != null) {
228+
posStack.push(result);
229+
}
230+
continue;
231+
}
232+
// token is illegal
233+
printInputError(token);
234+
}
235+
// all tokens have been processed
236+
if (posStack.isEmpty()) {
237+
return null;
238+
}
239+
return Long.parseLong(posStack.peek());
240+
}
241+
242+
/**
243+
* String Calculate function.
244+
*
245+
* @param expression input string to evaluate
246+
* @return a result as a string
247+
*/
248+
public static String stringCalculate(String expression) {
249+
String[] tokens = parseConsoleInput(expression);
250+
String[] postfix = convertToPostfix(tokens);
251+
Long result = evaluateExpression(postfix);
252+
return result == null ? "" : result.toString();
253+
}
254+
255+
/**
256+
* Entry point.
257+
*
258+
* @param args command line arguments
259+
*/
260+
public static void main(String[] args) {
261+
String expression1 = "7-4-1+8(3)/2";
262+
String result1 = stringCalculate(expression1);
263+
System.out.println(result1);
264+
String expression2 = "(2+2)*2";
265+
String result2 = stringCalculate(expression2);
266+
System.out.println(result2);
267+
String expression3 = "2+2*2";
268+
String result3 = stringCalculate(expression3);
269+
System.out.println(result3);
270+
}
271+
272+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy