Aritalab:Lecture/Programming/Java/Calculator
From Metabolomics.JP
< Aritalab:Lecture | Programming | Java(Difference between revisions)
m |
m |
||
| (2 intermediate revisions by one user not shown) | |||
| Line 1: | Line 1: | ||
| + | [[Aritalab:Lecture/Algorithm/PolishNotation|逆ポーランド記号]] (postfix) か通常の記法 (infix) で数式を記述できる計算機です。 | ||
| + | 四則演算のほかに log, sqrt をサポートしています。 | ||
| + | |||
| + | ; Calculator.java | ||
<pre> | <pre> | ||
import java.io.BufferedReader; | import java.io.BufferedReader; | ||
| Line 6: | Line 10: | ||
/** | /** | ||
| − | * @author | + | * @author masanori arita |
* 逆ポーランド記号電卓、および普通の記法の電卓 | * 逆ポーランド記号電卓、および普通の記法の電卓 | ||
*/ | */ | ||
Latest revision as of 09:43, 2 June 2011
逆ポーランド記号 (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();
}
}
}