解释器模式
给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。
JDK中的使用
- java.util.regex.Pattern#matcher(java.lang.CharSequence)
- java.text.MessageFormat#format(java.lang.String, java.lang.Object...)
- java.lang.String#format(java.lang.String, java.lang.Object...)
代码实现
输入一个计算表达式 计算出结果 代码不完整仅表达解释器这个意思
java
// 计算结果
@Data
@Builder
public class CalculateResult {
private boolean isValid;
private BigDecimal result;
public static CalculateResult invalid() {
return CalculateResult.builder()
.isValid(false)
.build();
}
public static CalculateResult valid(BigDecimal result) {
return CalculateResult.builder()
.isValid(true)
.result(result)
.build();
}
}
// 计算器帮助类
final class CalculateHelper {
private static final String LEFT_BRACKET = "(";
private static final String RIGHT_BRACKET = ")";
private static final String PLUS = "+";
private static final String MINUS = "-";
private static final String MULTIPLY = "*";
private static final String DIVIDE = "/";
private static final String REMAINDER = "%";
private static final String POINT = ".";
private CalculateHelper() {
}
static BigDecimal calculate(String expression) {
Stack<String> order = convertInOrder2PostOrder(expression);
return calc(order);
}
/**
* <p>
* 首先维护的是两个栈,我们这里暂且称为S1和S2,S1中的结果最后存的就是逆波兰表达式,
* S2中将用于暂时存放运算符并且在最终形成逆波兰表达式的时候,该栈是会清空的。
* </p>
* <ol>
* <li>
* 如果遇到的是数字,我们直接加入到栈S1中;
* </li>
* <li>
* 如果遇到的是左括号,则直接将该左括号加入到栈S2中;
* </li>
* <li>
* 如果遇到的是右括号,那么将栈S2中的运算符一次出栈加入到栈S1中,直到遇到左括号,
* 但是该左括号出栈S2并不加入到栈S1中;
* </li>
* <li>
* 如果遇到的是运算符,包括单目运算符和双目运算符,我们按照下面的规则进行操作:
* <ol>
* <li>
* 如果此时栈S2为空,则直接将运算符加入到栈S2中;
* </li>
* <li>
* 如果此时栈S2不为空,当前遍历的运算符的优先级大于等于栈顶运算符的优先级,那么直接入栈S2;
* </li>
* <li>
* 如果此时栈S2不为空,当前遍历的运算符的优先级小于栈顶运算符的优先级,则将栈顶运算符一直出栈加入到栈S1中,
* 直到栈为空或者遇到一个运算符的优先级小于等于当前遍历的运算符的优先级 此时将该运算符加入到栈S2中;
* </li>
* </ol>
* </li>
* <li>
* 直到遍历完整个中序表达式之后,栈S2中仍然存在运算符,那么将这些运算符依次出栈加入到栈S1中,直到栈为空。
* </li>
* </ol>
* @param expression 算式字符串
*/
private static Stack<String> convertInOrder2PostOrder(String expression) {
Stack<String> order = new Stack<>();
Stack<String> temp = new Stack<>();
for (int i = 0, len = expression.length(); i < len; i++) {
StringBuilder sb = new StringBuilder();
String word = expression.substring(i, i + 1);
if (isDigit(word)) {
sb.append(word);
for (int j = i + 1; j < len; j++) {
word = expression.substring(j, j + 1);
if (isDigit(word)) {
sb.append(word);
if (j == len - 1) {
i = len - 1;
break;
}
} else {
i = j - 1;
break;
}
}
order.push(sb.toString());
} else if (LEFT_BRACKET.equals(word)) {
temp.push(word);
} else if (RIGHT_BRACKET.equals(word)) {
while (temp.size() > 0) {
String top = temp.pop();
if (LEFT_BRACKET.equals(top)) {
break;
}
order.push(top);
}
} else { // + - * / %
if (temp.isEmpty()) {
temp.push(word);
} else {
String top = temp.peek();
if (compareTo(word, top) >= 0) {
temp.push(word);
} else {
while (temp.size() > 0) {
top = temp.peek();
if (compareTo(word, top) < 0) {
if (!LEFT_BRACKET.equals(top)) {
order.push(top);
}
} else {
temp.push(word);
break;
}
temp.pop();
}
}
}
}
}
if (!temp.isEmpty()) {
order.addAll(temp);
}
return order;
}
/**
* <p>
* 我们此时维护一个数据结果栈S3,我们将会看到该栈中最后存放的是最终的表达式的值。
* 我们从左至右的遍历栈S1,然后按照下面的规则进行操作栈S3.
* </p>
* <ol>
* <li>
* 如果遇到的是数字,那么直接将数字压入到S3中
* </li>
* <li>
* 如果遇到的是单目运算符,那么取S3栈顶的一个元素进行单目运算之后,将结果再次压入到栈S3中;
* </li>
* <li>
* 如果遇到的是双目运算符,那么取S3栈顶的两个元素进行,首先出栈的在左,后出栈的在右进行双目运算符的计算,将结果再次压入到S3中。
* </li>
* </ol>
* @param order
* @return
*/
private static BigDecimal calc(Stack<String> order) {
LinkedList<String> copyOrder = new LinkedList<>(order);
Stack<String> temp = new Stack<>();
while (copyOrder.size() > 0) {
String top = copyOrder.pollFirst();
if (isOperator(top)) {
String second = temp.pop();
String first = temp.pop();
temp.push(calc(first, second, top).toString());
} else {
temp.push(top);
}
}
return new BigDecimal(temp.pop());
}
private static BigDecimal calc(String first, String second, String operator) {
BigDecimal a = new BigDecimal(first);
BigDecimal b = new BigDecimal(second);
if (PLUS.equals(operator)) {
return a.add(b);
} else if (MINUS.equals(operator)) {
return a.subtract(b);
} else if (MULTIPLY.equals(operator)) {
return a.multiply(b);
} else if (DIVIDE.equals(operator)) {
return a.divide(b);
} else if (REMAINDER.equals(operator)) {
return a.remainder(b);
}
return null;
}
/**
* 是否是数字和小数点
* @param word 长度为1的字符串
* @return true/false
*/
private static boolean isDigit(String word) {
char c = word.toCharArray()[0];
return POINT.equals("" + c) || Character.isDigit(c);
}
/**
* 是否是运算符(+ - * / %)
* @param word 长度为1的字符串
* @return true/false
*/
private static boolean isOperator(String word) {
return PLUS.equals(word)
|| MINUS.equals(word)
|| MULTIPLY.equals(word)
|| DIVIDE.equals(word)
|| REMAINDER.equals(word);
}
/**
* 比较操作符优先级
* @param operator1 运算符1
* @param operator2 运算符2
* @return >0运算符1优先级大于运算符2 0优先级相同 <0运算符1优先级小于运算符2
*/
private static int compareTo(String operator1, String operator2) {
return getPriority(operator1) - getPriority(operator2);
}
/**
* 运算符优先级等级
* @param operator 运算符
* @return 1 0 -1
*/
private static int getPriority(String operator) {
if (LEFT_BRACKET.equals(operator) || RIGHT_BRACKET.equals(operator)) {
// ( )
return 1;
} else if (PLUS.equals(operator) || MINUS.equals(operator)) {
// + -
return -1;
} else {
// * / %
return 0;
}
}
}
public class Calculator {
@Getter
private String expression;
private static final String EXPRESSION_REG = "^(\\(*\\d+(.\\d+)*\\)*(\\+|-|/|\\*|%))+\\d+(.\\d+)*\\)*$";
public Calculator(String expression) {
this.expression = expression;
}
/**
* 简单校验是否是合法的算式
* @return true/false
*/
private boolean isValid() {
Pattern pattern = Pattern.compile(EXPRESSION_REG);
Matcher matcher = pattern.matcher(this.expression);
return matcher.matches();
}
/**
* 通过计算算式计算结果
* @return {@link CalculateResult}计算结果
*/
public CalculateResult calculate() {
if (!this.isValid()) {
return CalculateResult.invalid();
}
return CalculateResult.valid(CalculateHelper.calculate(this.expression));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
单元测试
groovy
class CalculatorSpec extends Specification {
def calculate() {
given:
def calculator = new Calculator(expression)
def result = calculator.calculate()
expect:
result.isValid() == isValid
result.getResult() == calculateResult
where:
expression || isValid || calculateResult
"a" || false || null
"1+++1" || false || null
"-1+1" || false || null
"--1+1" || false || null
"1+1" || true || 2
"1+(1-1)" || true || 1
"12.232+((111+333.222)-2*5)" || true || 446.454
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19