整理了一下代码和注释,优化了标识符,舍弃了一些不必要的格式修改(比如括号处理)
对堆栈接触少,看代码看了半天,自己觉得难懂的地方都加上了比较详细的注释
package calculator;
import java.util.Stack;
public class Calculator {
private Stack<Character> charStack = new Stack<Character>(); // 存储运算数、局部或最终运算结果
private Stack<Double> numStack = new Stack<Double>(); // 存储符号,包括括号及运算符,不包括小数点及数字
/**
* @Description 对五则运算表达式进行修改
*/
private String modify(String expression) {
// 判断表达式第一个字符是否为负号,若是,则在表达式前加0,便于后续计算,比如 -10 ===> 0-10
if (expression.charAt(0) == '-') {
expression = "0" + expression;
}
// 以'='结束
return (expression + "=");
}
/**
* @Description 根据数字和符号返回operationList操作表索引
*/
private int isSwitch(char oneChar) {
int number = 0;
switch (oneChar) {
case '+':
number = 0;
break;
case '-':
number = 1;
break;
case '*':
number = 2;
break;
case '/':
number = 3;
break;
case '(':
number = 4;
break;
case ')':
number = 5;
break;
case '^':
number = 6;
break;
case '=':
number = 7;
break;
}
return number;
}
/**
* @Description 根据addStack方法调用时传入的两个参数(当前符号、上一个运算符)返回应该执行的操作码
*/
private char Judge(char char1, char char2) {
/*
* 该操作表根据括号以及运算符优先级存储由当前位置符号及上一个运算符所决定的五种操作,即五个值:
* '>':一般是优先级相同或上一个运算符优先级更高,从左至右运算,即计算上一个运算符所在的二元表达式的结果,并将结果压入doubleStack栈顶部
* '<':与'>'相反,当前符号优先级更高,则跳过计算,将当前符号和上一个运算符压入栈中
* '=':输出最后结果(符号栈最后一个元素为手动压入的"=")
* 'K':开括号与回括号相撞,括号不入栈,比如 1+(2)+3
* 'E':多余括号错误,比如 1+(2= 和 1+)2=
*/
char[][] operationList = {
{ '>', '>', '<', '<', '<', '>', '<', '>' }, //'+'
{ '>', '>', '<', '<', '<', '>', '<', '>' }, //'-'
{ '>', '>', '>', '>', '<', '>', '<', '>' }, //'×'
{ '>', '>', '>', '>', '<', '>', '<', '>' }, //'÷'
{ '<', '<', '<', '<', '<', 'K', '<', 'E' }, //'('
{ '<', '<', '<', '<', '<', '<', '<', 'E' }, //')'
{ '>', '>', '>', '>', '<', '>', '>', '>' }, //'^'
{ '<', '<', '<', '<', '<', '<', '<', '=' } //'='
};
return operationList[isSwitch(char1)][isSwitch(char2)]; // 返回操作码
}
/**
* @Description 栈计算
*/
private String addStack(String expression) {
int length = expression.length();
char oneChar = 0;
// 栈以'='作为第一个元素(先进后出)
charStack.push('=');
/*
* 确定一个数的大小,小数还需确定小数点后位数
* 例如3.76,以 number = 376.0 及 decimalTime = 100 存储,即 3.76 = 376.0 / 100
*/
int i = 0;
boolean numberFlag = false, // 数字遍历标记
decimalFlag = false; // 识别一个运算数时是否遇到小数点
double singleNum = 0, // 存放一个数字
number = 0, // 存放一个运算数(无小数部分)
decimalTime = 1; // 该运算数的小数位数(倍数)
while (i < length) {
oneChar = expression.charAt(i);
while (oneChar >= '0' && oneChar <= '9' || oneChar == '.') {
if (oneChar != '.') {
singleNum = Double.parseDouble(String.valueOf(oneChar)); // 将单个数字字符转换为字符串再转换为浮点型数据
number = (number * 10) + singleNum;
numberFlag = true; // 遍历到数字,标记置真
if (decimalFlag)
decimalTime *= 10;
} else
decimalFlag = true; // 遍历到小数点,标记置真
i++;
oneChar = expression.charAt(i);
}
boolean singleReverse = false, // 多个运算数正负调换标记,比如 -(3+2) ==> (-3+-2)
multiReverse = false; // 单个运算数正负调换标记,针对单个负数,即'-'后为数字的情况
if (numberFlag) {
// 正负调换
if (singleReverse || multiReverse) {
if (singleReverse)
singleReverse = false; // 单个负数置正完成,标记重置
number = -number;
}
// 若为小数则生成小数运算数
if (decimalFlag) {
number /= decimalTime;
decimalFlag = false;
decimalTime = 1;
}
numStack.push(number); // 将运算数压入栈
number = 0; // 运算数重置
numberFlag = false; // 运算数压入栈中,标记重置
}
if (multiReverse && oneChar == ')') // 遇到回括号,表示'-'所管理的括号内的所有运算数正负调换已完成,多个运算数正负调换标记重置
multiReverse = false;
boolean reckonAllow = true; // 计算操作允许
if (oneChar == '-') {
char lastChar = expression.charAt(i - 1);
char nextChar = expression.charAt(i + 1);
// '-'前是符号
if (lastChar == '+' || lastChar == '-' || lastChar == '*' || lastChar == '/' || lastChar == '(') {
// '-'后是开括号,则计算时忽略该负号并将括号内表达式运算数的正负调换,见142至146行,比如 5*-(10+4) ==> 5*(-10+-4)
if (nextChar == '(')
multiReverse = true;
// '-'后是数字,则将负号后的运算数正负调换,见142至146行
else
singleReverse = true;
reckonAllow = false;
}
}
// 栈计算
double result = 0, num1 = 0, num2 = 0;
char recentPop;
while (reckonAllow) {
// oneChar存放当前位置符号,nextPop存储上一个运算符,比如表达式为 1+2= ,此时遍历至 = ,则 oneChar 为 = ,
// nextPop为2
recentPop = charStack.pop(); // 上一个运算符
oneChar = expression.charAt(i); // 一定为除'-'和数字之外的符号
switch (Judge(recentPop, oneChar)) {
case '>':
try {
num2 = numStack.pop();
num1 = numStack.pop();
} catch (Exception error) {
return "输入格式错误";
}
switch (recentPop) {
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
result = num1 / num2;
break;
case '^':
result = Math.pow(num1, num2);
break;
}
numStack.push(result);
break;
case '<':
charStack.push(recentPop);
charStack.push(oneChar);
reckonAllow = false;
break;
case '=':
expression = expression.replace('*', '×'); // '*'替换'×'
expression = expression.replace('/', '÷'); // '/'替换'÷'
if (expression.charAt(0) == '0') // 忽略开头的0
expression = expression.substring(1, expression.length());
double finalResult = numStack.pop();
if ((int) finalResult == finalResult)
return (expression + (int) finalResult); // 若计算结果为整数则类型转换为整型后输出
else
return (expression + finalResult);
case 'K':
i++;
break;
case 'E':
return ("输入格式错误");
}
}
i++;
}
return null;
}
/**
* @Description 调用类私有方法计算结果
*/
public String getResult(String expression) {
return addStack(modify(expression.trim())); // trim()方法去除表达式前后空白
}
}