当年java课程的大作业是写一个计算器,我花了两个多星期总算完成了,但是却只写了一个类,代码达1500多行,压根纯粹没用到java的面向对象思想,现在想想还有点惭愧
最近想起了这个事情,于是想抽空写一个完整的面向对象的科学计算器,完成以前未了的心愿
稍微花了点时间整理了一下思路:
1.首先得将表达式分为操作数和运算符,这里用正则即可
private String regex = "\\^|(\\d+\\.\\d+)|(\\d+)|(\\+)|(-)|(\\*)|(/)|(\\()|(\\))|sin|cos|tan|\\!";
当然我只是写了一部分运算符,其他的类似
2.运算符和操作数统一都继承了Item类,而所有的运算符类又继承了Operator类,其实所有运算符可以用一个类来表示,但是为了更明确的突出面向对象,一种运算符一个类,并且有优先级和操作数个数等属性
3.本来四则运算是可以转成逆波兰式计算的,但是这里还包括了三角函数,阶乘,乘方等非四则运算的运算符,标准的逆波兰式显然不适合,于是重新设计了思路如下:
1)首先去括号,获取最里层的括号,计算里面的无括号表达式,得到结果后替换这一部分,然后递归直到去掉所有的括号,最后剩下一个无括号的表达式,跟之前去括号时调用同样的计算方法。
2)无括号的表达式采用这样的计算方式,遍历表达式,获取最高优先级的运算符,并根据运算符获取前后的操作数(三角函数取后面一个,阶乘取前面一个,其他前后各取一个),然后根据Operator类里定义的计算方法获得结果,然后替换计算过的部分,递归进行下一步计算,直到整个表达式只剩下一个操作数,那么这就是最后的计算结果。
计算无括号表达式的方法
private Operand calculate(List<Item> subList) {
if (subList.size() == 0) {
return null;
} else if (subList.size() == 1 && subList.get(0) instanceof Operand) {
return (Operand) subList.get(0);
} else {
Operator operator = null;
int index = -1;
int priority = -1;
for (int i = 0; i < subList.size(); i++) {
Item item = subList.get(i);
if (item instanceof Operator) {
if (((Operator) item).getPriority() > priority) {
operator = (Operator) item;
index = i;
priority = operator.getPriority();
}
}
}
if (operator instanceof Factorial) {
operator.setOperand1((Operand) subList.get(index - 1));
subList.add(index, operator.getResult());
} else {
if (operator.getOperandCount() == 1) {
operator.setOperand1((Operand) subList.get(index + 1));
subList.add(index, operator.getResult());
} else if (operator.getOperandCount() == 2) {
operator.setOperand1((Operand) subList.get(index - 1));
operator.setOperand2((Operand) subList.get(index + 1));
subList.add(index, operator.getResult());
}
}
subList.remove(operator);
subList.remove(operator.getOperand1());
subList.remove(operator.getOperand2());
return calculate(subList);
}
}
3) 其中要考虑一些细节问题,比如表达式不正确会导致数组越界,比如有些地方可以省略乘号等,整个计算器的设计思想算是完成了,而且添加其他的运算符也是很容易的