Aritalab:Lecture/Programming/Java/Calculator
From Metabolomics.JP
< Aritalab:Lecture | Programming | Java
逆ポーランド記号 (postfix) か通常の記法 (infix) で数式を記述できる計算機です。 四則演算のほかに log, sqrt をサポートしています。
- Calculator.java
import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Stack; import java.util.StringTokenizer; /** * @author masanori arita * 逆ポーランド記号電卓、および普通の記法の電卓 */ public class Calculator { static final int NUMBER = 0; /** * 与えられた文字が数字なのか、オペレータなのかを判定する関数 * オペレータの場合は優先度を返し、数字の場合はNUMBER (優先度は一番低い) * * @param op * トークン(数かオペレータ) * @return トークンの優先度 */ int getPriority(String op) { final String[] functions = { "+", "-", "/", "*", "log", "sqrt" }; final int[] priority = { 1, 1, 2, 2, 3, 3 }; for (int i = 0; i < functions.length; i++) if (op.equals(functions[i])) return priority[i]; return NUMBER; } /** * tokenStackの先頭からnextPrioより優先度が高い部分を popして計算 * * @param nextPrio * 次に来ているトークンの優先度 * @param stackPrio * スタックの先頭にあるオペレータの優先度 * @param tokenStack * トークンが入っているスタック * @throws Exception */ void reduce(int nextPrio, int stackPrio, Stack<String> tokenStack) throws Exception { while ((tokenStack.size() > 1) && (nextPrio <= stackPrio)) { // 1つめのtoken double num1 = Double.parseDouble(tokenStack .pop()); // 2つめのtoken String op = tokenStack.pop(); /** ()の対応用 */ if (op.equals("(")) { // 左カッコを発見したら強制的に数式の終わりとみなす // 左カッコはここで捨てられる tokenStack.push("" + num1); return; } switch (getPriority(op)) { case 0: throw new Exception("オペレータが足りません"); case 3: // 関数適用 if (op.equals("log")) num1 = Math.log(num1); else if (op.equals("sqrt")) num1 = Math.sqrt(num1); break; case 1: case 2: // 四則演算 if (tokenStack.size() == 0) throw new Exception("オペランドが足りません"); double num2 = Double.parseDouble(tokenStack .pop()); if (op.equals("/")) { if (num1 == 0) throw new Exception("0で割り算しています"); num1 = num2 / num1; } else if (op.equals("*")) num1 = num2 * num1; else if (op.equals("+")) num1 = num2 + num1; else if (op.equals("-")) num1 = num2 - num1; break; } tokenStack.push("" + num1); } } /** * 入力された数式を読んで計算する関数 * @param st トークンの入力列 * @return 計算結果 * @throws Exception */ double parse(StringTokenizer st) throws Exception { // トークンをためるスタックを初期化 Stack<String> tokenStack = new Stack<String>(); int stackTopPrio = 0; while (st.hasMoreTokens()) { // 次に処理するトークンを先読み String token = st.nextToken(); /** ()の対応用 */ if (token.equals(")")) // 右カッコが出てきたら、入力文字列の最後とみなす break; else if (token.equals("(")) // 左カッコが出てきたら、新しい数式が始まったとみなす // その結果を1つの数字とみなす tokenStack.push("" + parse(st)); else // 演算か数式 { int nextPrio = getPriority(token); // もし先読みしたトークンよりスタックに積んである式のほうが // 優先度が高い場合、スタックの中身を先に処理 if ((nextPrio != NUMBER) && (nextPrio <= stackTopPrio)) reduce(nextPrio, stackTopPrio, tokenStack); tokenStack.push(token); if (nextPrio != NUMBER) stackTopPrio = nextPrio; } } // 入力文字列の最後にきたので、スタックに残っている式を処理 reduce(0, stackTopPrio, tokenStack); if (tokenStack.size() != 1) throw new Exception("オペレータが足りません"); return Double.parseDouble(tokenStack.pop()); } public double parseRPN(StringTokenizer st) throws Exception { Stack<String> tokenStack = new Stack<String>(); while (st.hasMoreTokens()) { String nextToken = st.nextToken(); if (nextToken.equals("+")) { if (tokenStack.size() < 2) throw new Exception("オペランドが足りません"); double d1 = Double.parseDouble(tokenStack.pop()); double d2 = Double.parseDouble(tokenStack.pop()); tokenStack.push("" + (d2 + d1)); } else if (nextToken.equals("-")) { if (tokenStack.size() < 2) throw new Exception("オペランドが足りません"); double d1 = Double.parseDouble(tokenStack.pop()); double d2 = Double.parseDouble(tokenStack.pop()); tokenStack.push("" + (d2 - d1)); } else if (nextToken.equals("log")) { if (tokenStack.size() < 1) throw new Exception("オペランドが足りません"); double d1 = Double.parseDouble(tokenStack.pop()); tokenStack.push("" + Math.log(d1)); } else tokenStack.push(nextToken); } //最後にスタックには数字が一つ残っているはず if (tokenStack.size() != 1) throw new Exception("オペレータが足りません"); return Double.parseDouble(tokenStack.pop()); } public static void main(String[] args) { try { BufferedReader br = new BufferedReader( new InputStreamReader(System.in)); while (true) { System.out.print(">"); String line = br.readLine(); if (line == null) break; StringTokenizer st = new StringTokenizer( line); try { Calculator C = new Calculator(); //System.out.println(C.parseRPN(st)); // 逆ポーランド電卓 System.out.println(C.parse(st)); // 普通の電卓 } catch (Exception e) { System.out.println("Error = " + e); } } } catch (Exception exp) { exp.printStackTrace(); } } }