回答編集履歴

1

コメントの回答追加

2016/10/20 17:17

投稿

naomi3
naomi3

スコア1105

test CHANGED
@@ -1,38 +1,40 @@
1
- 演算子の優先順位が同じで、( )を使わない、電卓のような場合は比較的単純に修正できます。
2
-
3
1
  ```Java
4
2
 
5
3
  import java.io.BufferedReader;
6
4
 
7
5
  import java.io.InputStreamReader;
8
6
 
7
+ import java.util.ArrayDeque;
8
+
9
+ import java.util.EmptyStackException;
10
+
11
+ import java.util.HashMap;
12
+
13
+ import java.util.Stack;
14
+
9
15
  import java.util.StringTokenizer;
10
16
 
11
17
 
12
18
 
13
19
  // Calcクラス
14
20
 
15
- public class Calc {
21
+ public class Calc
22
+
16
-
23
+ {
17
-
18
24
 
19
25
  // メインメソッド
20
26
 
21
- public static void main(String[] args) {
27
+ public static void main(String[] args)
28
+
22
-
29
+ {
23
-
24
-
30
+
25
- try {
31
+ try
26
-
32
+
27
- int ans = 0;
33
+ {
28
-
29
-
30
34
 
31
35
  InputStreamReader isr = new InputStreamReader(System.in);
32
36
 
33
-
34
-
35
- BufferedReader br = new BufferedReader(isr);
37
+ BufferedReader bReader = new BufferedReader(isr);
36
38
 
37
39
 
38
40
 
@@ -40,25 +42,41 @@
40
42
 
41
43
 
42
44
 
45
+ for (; ;)
46
+
47
+ {
48
+
49
+ // プロンプトの出力
50
+
51
+ System.out.print(">");
52
+
53
+
54
+
43
- for (String expr = br.readLine();
55
+ String expr = bReader.readLine();
44
-
56
+
45
- expr != null && ! expr.equals("");
57
+ if (expr == null || expr.equals(""))
58
+
46
-
59
+ {
60
+
47
- expr = br.readLine()) {
61
+ break;
62
+
48
-
63
+ }
49
-
50
-
64
+
65
+
66
+
51
- try {
67
+ try
68
+
52
-
69
+ {
70
+
53
- ans = calc.calc(expr);
71
+ int ans = calc.calc(expr);
54
72
 
55
73
  System.out.println("=" + ans);
56
74
 
57
75
  }
58
76
 
59
- catch (UsrExprException e) {
77
+ catch (UsrExprException e)
78
+
60
-
79
+ {
61
-
62
80
 
63
81
  System.out.println("式に誤りがあります : " + e.getMessage());
64
82
 
@@ -66,49 +84,149 @@
66
84
 
67
85
  }
68
86
 
69
- }
70
-
71
- catch (Exception e) {
72
-
73
-
74
-
75
- System.out.println("例外"+ e);
76
-
77
- }
78
-
79
- }
80
-
81
-
82
-
83
- public int calc(String expr) throws Exception {
84
-
85
-
86
-
87
- int ans = 0;
88
-
89
-
90
-
91
- StringTokenizer tokenizer = new StringTokenizer(expr, "+-*/", true);
92
-
93
-
94
-
95
- String sign = "+";
96
-
97
- String operator = "+";
98
-
99
-
100
-
101
- while (tokenizer.hasMoreTokens()) {
102
-
103
-
104
-
105
- String token = tokenizer.nextToken().trim();
106
-
107
-
108
-
109
- if (token.length() == 0) {
110
-
111
-
87
+ }
88
+
89
+ catch (Exception e)
90
+
91
+ {
92
+
93
+ System.out.println("例外 : " + e);
94
+
95
+ }
96
+
97
+
98
+
99
+ System.out.println("終了");
100
+
101
+ }
102
+
103
+
104
+
105
+
106
+
107
+ public int calc(String expr) throws Exception
108
+
109
+ {
110
+
111
+ // 全ての空白文字を取り除く
112
+
113
+ expr = expr.replaceAll("\\s+", "");
114
+
115
+
116
+
117
+ boolean printRevPolishExpr = false;
118
+
119
+ // p で始まっていたら、逆ポーランド記法表示モード
120
+
121
+ if (expr.startsWith("p"))
122
+
123
+ {
124
+
125
+ printRevPolishExpr = true;
126
+
127
+ expr = expr.substring(1);
128
+
129
+ }
130
+
131
+
132
+
133
+ // 字句解析
134
+
135
+ StringTokenizer tokenizer = new StringTokenizer(expr, "+-/*", true);
136
+
137
+
138
+
139
+ ArrayDeque<String> tokenQueue = new ArrayDeque<>();
140
+
141
+
142
+
143
+ while (tokenizer.hasMoreTokens())
144
+
145
+ {
146
+
147
+ String token = tokenizer.nextToken();
148
+
149
+
150
+
151
+ if (! token.isEmpty())
152
+
153
+ {
154
+
155
+ tokenQueue.add(token);
156
+
157
+ }
158
+
159
+ }
160
+
161
+
162
+
163
+ tokenQueue = replaceAsUnaryOps(tokenQueue);
164
+
165
+
166
+
167
+ ArrayDeque<String> revPolishQueue = toRevPolish(tokenQueue);
168
+
169
+
170
+
171
+ if (printRevPolishExpr)
172
+
173
+ {
174
+
175
+ ArrayDeque<String> revPolishExpr = revPolishQueue.clone();
176
+
177
+
178
+
179
+ while (! revPolishExpr.isEmpty())
180
+
181
+ {
182
+
183
+ System.out.print(" " + revPolishExpr.remove());
184
+
185
+ }
186
+
187
+ System.out.println();
188
+
189
+ }
190
+
191
+
192
+
193
+ return evaluate(revPolishQueue);
194
+
195
+ }
196
+
197
+
198
+
199
+
200
+
201
+ // 単項演算子を見つけて置換する
202
+
203
+ protected static ArrayDeque<String> replaceAsUnaryOps(ArrayDeque<String> tokenQueue)
204
+
205
+ {
206
+
207
+ ArrayDeque<String> outQueue = new ArrayDeque<>();
208
+
209
+
210
+
211
+ // 前回の演算子を"+"に初期化
212
+
213
+ String lastOperator = "+";
214
+
215
+
216
+
217
+ while (! tokenQueue.isEmpty())
218
+
219
+ {
220
+
221
+ String token = tokenQueue.remove();
222
+
223
+ if (isNumber(token))
224
+
225
+ {
226
+
227
+ lastOperator = null;
228
+
229
+ outQueue.add(token);
112
230
 
113
231
  continue;
114
232
 
@@ -116,149 +234,493 @@
116
234
 
117
235
 
118
236
 
237
+ // tokenは演算子
238
+
239
+
240
+
241
+ if (lastOperator == null)
242
+
243
+ {
244
+
245
+ lastOperator = token;
246
+
247
+ outQueue.add(token);
248
+
119
- int num;
249
+ continue;
250
+
120
-
251
+ }
252
+
253
+
254
+
121
-
255
+ // 前回も演算子
256
+
257
+
258
+
122
-
259
+ switch (token)
260
+
261
+ {
262
+
263
+ case "+":
264
+
265
+ // 何もしないので詰めない
266
+
267
+ break;
268
+
269
+
270
+
271
+ case "-":
272
+
123
- if (! token.matches("[-+*/]")) {
273
+ if (lastOperator.equals("u-"))
274
+
124
-
275
+ {
276
+
125
-
277
+ // 2回連続"u-"である場合
278
+
279
+
280
+
126
-
281
+ // 前回詰めた"u-"を捨てる
282
+
283
+ outQueue.removeLast();
284
+
285
+
286
+
287
+ // 前々回詰めた演算子を復元する
288
+
289
+ lastOperator = outQueue.peekLast();
290
+
291
+ if (lastOperator == null)
292
+
293
+ {
294
+
127
- // トークンが演算子でなければ、整数と解釈し、演算行う
295
+ // 前回の演算子を"+"に再初期化
128
-
129
- try {
296
+
130
-
131
- num = Integer.parseInt(token);
297
+ lastOperator = "+";
298
+
299
+ }
132
300
 
133
301
  }
134
302
 
303
+ else
304
+
305
+ {
306
+
135
- catch (NumberFormatException e) {
307
+ lastOperator = "u-";
136
-
137
-
138
-
308
+
139
- throw new UsrExprException("整数ではない");
309
+ outQueue.add("u-");
140
310
 
141
311
  }
142
312
 
143
-
313
+ break;
314
+
315
+
316
+
144
-
317
+ default:
318
+
319
+ lastOperator = token;
320
+
145
- if (sign.equals("-")) {
321
+ outQueue.add(token);
322
+
146
-
323
+ }
324
+
325
+ }
326
+
327
+
328
+
147
- num = - num;
329
+ return outQueue;
330
+
331
+ }
332
+
333
+
334
+
335
+
336
+
337
+ // 式を逆ポーランド記法に変換する
338
+
339
+ protected static ArrayDeque<String> toRevPolish(ArrayDeque<String> tokenQueue)
340
+
341
+ throws UsrExprException
342
+
343
+ {
344
+
345
+ ArrayDeque<String> outQueue = new ArrayDeque<>();
346
+
347
+ Stack<String> stack = new Stack<>();
348
+
349
+
350
+
351
+ while (! tokenQueue.isEmpty())
352
+
353
+ {
354
+
355
+ String token = tokenQueue.remove();
356
+
357
+
358
+
359
+ if (isNumber(token))
360
+
361
+ {
362
+
363
+ // 数値ならば無条件にキューに書き出す
364
+
365
+ outQueue.add(token);
366
+
367
+ continue;
368
+
369
+ }
370
+
371
+
372
+
373
+ // tokenは演算子
374
+
375
+
376
+
377
+ if (stack.isEmpty())
378
+
379
+ {
380
+
381
+ // スタックが空ならば無条件にスタックに積む
382
+
383
+ stack.push(token);
384
+
385
+ continue;
386
+
387
+ }
388
+
389
+
390
+
391
+ // スタックは空ではない
392
+
393
+
394
+
395
+ // tokenの優先順位を得る
396
+
397
+ int priorityOfToken = getPriorityOf(token);
398
+
399
+
400
+
401
+ // スタックの先頭を得る(ただし取り出さない)
402
+
403
+ String topOfStack = stack.peek();
404
+
405
+ if (priorityOfToken > getPriorityOf(topOfStack))
406
+
407
+ {
408
+
409
+ // スタックの先頭より優先順位が高ければスタックに積む
410
+
411
+ stack.push(token);
412
+
413
+ continue;
414
+
415
+ }
416
+
417
+
418
+
419
+ do
420
+
421
+ {
422
+
423
+ // スタックの先頭を取り出してキューに書き出す
424
+
425
+ topOfStack = stack.pop();
426
+
427
+ outQueue.add(topOfStack);
428
+
429
+
430
+
431
+ // スタックの先頭より優先順位が低くなるまで繰り返す
432
+
433
+ if (priorityOfToken > getPriorityOf(topOfStack))
434
+
435
+ {
436
+
437
+ break;
148
438
 
149
439
  }
150
440
 
151
-
441
+ }
442
+
152
-
443
+ while (! stack.isEmpty());
444
+
445
+
446
+
447
+ // スタックに積む
448
+
449
+ stack.push(token);
450
+
451
+ }
452
+
453
+
454
+
455
+ // スタックに残っている全てを取り出してキューに書き出す
456
+
457
+ while (! stack.isEmpty())
458
+
459
+ {
460
+
461
+ String topOfStack = stack.pop();
462
+
463
+ outQueue.add(topOfStack);
464
+
465
+ }
466
+
467
+
468
+
469
+ return outQueue;
470
+
471
+ }
472
+
473
+
474
+
475
+ protected static boolean isNumber(String token)
476
+
477
+ {
478
+
479
+ try
480
+
481
+ {
482
+
483
+ Integer.parseInt(token);
484
+
485
+ return true;
486
+
487
+ }
488
+
489
+ catch (NumberFormatException e)
490
+
491
+ {
492
+
493
+ return false;
494
+
495
+ }
496
+
497
+ }
498
+
499
+
500
+
501
+
502
+
503
+ // 演算子の優先順位を定義
504
+
505
+ protected static final HashMap<String, Integer> priorityMap =
506
+
507
+
508
+
509
+ new HashMap<String, Integer>() {
510
+
511
+ {
512
+
513
+ // ※数値が高いほど、優先順位が高いとする
514
+
515
+ put("u+", 3); // 単項演算子
516
+
517
+ put("u-", 3); // 単項演算子
518
+
519
+ put("*", 2);
520
+
521
+ put("/", 2);
522
+
153
- if (operator.equals("+")) {
523
+ put("+", 1);
524
+
154
-
525
+ put("-", 1);
526
+
155
-
527
+ }
528
+
156
-
529
+ };
530
+
531
+
532
+
533
+
534
+
535
+ // 演算子の優先順位を得る
536
+
537
+ protected static int getPriorityOf(String operator)
538
+
539
+ throws UsrExprException
540
+
541
+ {
542
+
543
+ Integer priority = priorityMap.get(operator);
544
+
545
+ // 見つからない場合はnullを返す
546
+
547
+
548
+
549
+ if (priority != null)
550
+
551
+ {
552
+
553
+ return priority;
554
+
555
+ }
556
+
557
+
558
+
559
+ throw new UsrExprException("'" + operator + "':未知の演算子");
560
+
561
+ }
562
+
563
+
564
+
565
+
566
+
567
+ // 逆ポーランド記法に変換された式を評価する
568
+
569
+ protected static int evaluate(ArrayDeque<String> revPolishQueue)
570
+
571
+ throws UsrExprException
572
+
573
+ {
574
+
157
- ans += num;
575
+ int ans;
576
+
577
+ ValueStack stack = new ValueStack();
578
+
579
+
580
+
581
+ try
582
+
583
+ {
584
+
585
+ while (! revPolishQueue.isEmpty())
586
+
587
+ {
588
+
589
+ String token = revPolishQueue.remove();
590
+
591
+
592
+
593
+ if (isNumber(token))
594
+
595
+ {
596
+
597
+ // 数値ならば無条件にスタックに積む
598
+
599
+ stack.push(token);
600
+
601
+ continue;
158
602
 
159
603
  }
160
604
 
161
- else if (operator.equals("-")) {
605
+
162
-
163
-
164
-
606
+
165
- ans -= num;
607
+ int operand1;
608
+
609
+ int operand2;
610
+
611
+
612
+
613
+ switch (token)
614
+
615
+ {
616
+
617
+ case "u+": // 単項 + 演算子
618
+
619
+ // スタックはそのまま
620
+
621
+ break;
622
+
623
+
624
+
625
+ case "u-": // 単項 - 演算子
626
+
627
+ operand1 = stack.pop();
628
+
629
+ // 演算結果をスタックに積む
630
+
631
+ stack.push(- operand1);
632
+
633
+ break;
634
+
635
+
636
+
637
+ case "+":
638
+
639
+ operand2 = stack.pop();
640
+
641
+ operand1 = stack.pop();
642
+
643
+ // 演算結果をスタックに積む
644
+
645
+ stack.push(operand1 + operand2);
646
+
647
+ break;
648
+
649
+
650
+
651
+ case "-":
652
+
653
+ operand2 = stack.pop();
654
+
655
+ operand1 = stack.pop();
656
+
657
+ // 演算結果をスタックに積む
658
+
659
+ stack.push(operand1 - operand2);
660
+
661
+ break;
662
+
663
+
664
+
665
+ case "*":
666
+
667
+ operand2 = stack.pop();
668
+
669
+ operand1 = stack.pop();
670
+
671
+ // 演算結果をスタックに積む
672
+
673
+ stack.push(operand1 * operand2);
674
+
675
+ break;
676
+
677
+
678
+
679
+ case "/":
680
+
681
+ operand2 = stack.pop();
682
+
683
+ operand1 = stack.pop();
684
+
685
+ // 演算結果をスタックに積む
686
+
687
+ stack.push(operand1 / operand2);
688
+
689
+ break;
166
690
 
167
691
  }
168
692
 
169
- else if (operator.equals("*")) {
170
-
171
-
172
-
173
- ans *= num;
174
-
175
- }
176
-
177
- else if (operator.equals("/")) {
178
-
179
-
180
-
181
- ans /= num;
182
-
183
- }
184
-
185
-
186
-
187
- operator = null;
188
-
189
- }
190
-
191
- else {
192
-
193
- // トークンが演算子の場合
194
-
195
-
196
-
197
- if (operator == null) {
198
-
199
-
200
-
201
- // 前回が演算子でなければ、演算子を保存
202
-
203
- operator = token;
204
-
205
-
206
-
207
- // 符号に + をセット
208
-
209
- sign = "+";
210
-
211
- }
212
-
213
- else {
214
-
215
-
216
-
217
- // 前回が演算子の場合
218
-
219
-
220
-
221
- if (token.equals("-") || token.equals("+")) {
222
-
223
-
224
-
225
- // トークンが符号であれば、演算子はそのまま
226
-
227
- // 符号にトークンをセット
228
-
229
- sign = token;
230
-
231
- }
232
-
233
- else {
234
-
235
-
236
-
237
- throw new UsrExprException("演算子の次が符号でない");
238
-
239
- }
240
-
241
- }
242
-
243
- }
244
-
245
- }
246
-
247
-
248
-
249
- // 式の最後に整数がない場合
250
-
251
- if (operator != null) {
252
-
253
-
254
-
255
- throw new UsrExprException("式の最後に整数がない");
256
-
257
- }
258
-
259
-
260
-
261
- //ansを返す
693
+ }
694
+
695
+
696
+
697
+ // 最終結果をスタックから取り出す
698
+
699
+ ans = stack.pop();
700
+
701
+ }
702
+
703
+ catch (EmptyStackException e)
704
+
705
+ {
706
+
707
+ throw new UsrExprException("数値が足りない");
708
+
709
+ }
710
+
711
+
712
+
713
+ if (! stack.empty())
714
+
715
+ {
716
+
717
+ throw new UsrExprException("演算子が足りない");
718
+
719
+ }
720
+
721
+
722
+
723
+ // ansを返す
262
724
 
263
725
  return ans;
264
726
 
@@ -266,10 +728,104 @@
266
728
 
267
729
  }
268
730
 
731
+
732
+
733
+
734
+
735
+ import java.util.EmptyStackException;
736
+
737
+ import java.util.Stack;
738
+
739
+
740
+
741
+
742
+
743
+ public class ValueStack {
744
+
745
+
746
+
747
+ private Stack<Integer> stack = new Stack<>();
748
+
749
+
750
+
751
+
752
+
753
+ public ValueStack() {
754
+
755
+ }
756
+
757
+
758
+
759
+
760
+
761
+ public int push(int item) {
762
+
763
+ return stack.push(item);
764
+
765
+ }
766
+
767
+
768
+
769
+
770
+
771
+ public int push(String item) throws NumberFormatException {
772
+
773
+
774
+
775
+ Integer num = Integer.parseInt(item);
776
+
777
+
778
+
779
+ if (num == null) {
780
+
781
+ throw new NullPointerException();
782
+
783
+ }
784
+
785
+
786
+
787
+ return stack.push(num);
788
+
789
+ }
790
+
791
+
792
+
793
+
794
+
795
+ public int pop() throws EmptyStackException {
796
+
797
+ return stack.pop();
798
+
799
+ }
800
+
801
+
802
+
803
+
804
+
805
+ public boolean empty() {
806
+
807
+ return stack.empty();
808
+
809
+ }
810
+
811
+ }
812
+
269
813
  ```
270
814
 
815
+
816
+
271
- 優先順位や( )を考慮する場合は、根本的な修正が必要です。
817
+ キュー、スタックデータ構造でよく使われます。
818
+
272
-
819
+ case 文字列: … break; はJavaのバージョンによって使えない場合があります。
820
+
273
- その場合、式を逆ポーランド記法へ換しから、演算を行うと簡単でしょう
821
+ else if (token.equals(文字列)) { … } にください
274
-
822
+
823
+
824
+
275
- 逆ポーランド記法への変換アルゴリズム知られていので、調べてください
825
+ 逆ポーランド記法変換するメソッドを少し変えるだけで( )扱えようになります
826
+
827
+ 優先順位を正しく設定すると、Javaのほかの演算子、%, &, |, ^, ~などにも対応できます。
828
+
829
+ 字句解析を正規表現によるマッチングに変えると、浮動小数点、複数文字の演算子、>>, >>>, <<やJavaにはない演算子、例えばべき乗 ** も扱えるようになりますね。
830
+
831
+