今天来解决基础计算问题。
通过这几天对计算器的把玩(确实是“把玩”,朋友看我捧个计算器按下1+2=3然后托腮冥想的表情时都投来了崇拜的目光…问我你是在怀疑人生么…),尝试了几种描述工作方式的模型,以下一种是我认为最直观的:
首先是发动机盖下面的东西:)
开机后输入1+2=时,计算器都干了什么呢?
- 开机;
- 把1写入操作数1;
- 把+写入计算符号;
- 把2写入操作数2;
- 运行计算操作;
- 把计算结果3保存到结果中,并返回到显示屏。
实际应用时要考虑的东西就更多了,因为它不能只支持一次计算呀。比如:
显示屏上或者其他组件里随时都有可能存着其他东西,在任意时刻按下按钮时计算器应做出什么反应?比如:按下一个数字时,是要接着显示屏中现有的数字往下输,还是新开始一个数字?按下一个运算符时,是准备进行一次全新的计算呢,还是刚才输错了准备修改呢,还是要进行连续计算呢?
计算器是支持连续计算的,如果你输入了1+2,不停地按下=,你会得到3,5,7,9……是怎么实现的?
按下1+后直接按=可以得到2,计算器是怎么自行脑补“操作数2”的数值为1的呢?
一个数字里不能在开头出现多个0,如果有一个小数点后就不能再有第二个小数点,如何保证?
……
就问你晕不晕。
真正足够通用的、可以称得上工业化的计算器算法应该是基于状态机的,涉及到一定的编译原理知识,作为编程小白的我表示怕怕。通过我自己的总结,写了一套比较浅显的,能基本运行的算法,也算“原曲不使用”了吧:
【初始化条件】
“显示屏”显示“0”,flag“倒下”,“操作数1”、“运算符”、“操作数2”、“结果”均为空。
【按下数字键[0-9.]时的算法】
- 如果“结果”存在,则清除所有组件的内容;
- 如果flag立起,则清除“显示屏”的内容,显示“0”,放倒flag;
- 如果“显示屏”显示“0”且按下的数字键是[0-9],则清除“显示器”的内容;
- 如果“显示屏”中的内容包含“.”且按下的数字键是“.”,则清除按下键的内容;
- 将当前按下键的内容添加到“显示器”内容的末尾;
- 若“运算符”为空,则将“操作数1”赋值为当前“显示器”内容,否则将“操作数2”赋值为当前“显示器”内容。
下面分别分析一下:
- 如果“结果”存在,则证明刚完成一次计算,(这一因果关系如何保证?就是由1本身保证的,因为只要发现“结果”存在,它立刻就被清除了。)此时需要进行新的计算了,刚才的所有数据都没有保存的必要了;
- 为什么要有这个flag?(这里立flag可不是为了打脸的:)它是[+-*/]的助手。设想一下如果没有这个flag,输入1+后“显示器”显示的仍是1,此时如果你想输2会接着现有的1而得到12,这不是你想要的。因此必须有个标志物,在刚刚按下“运算符”时点亮,其它时刻熄灭;
- 为了清除错误的输入,数字左端不能有多个0;
- 数字中不能有多个小数点;
- 核心步骤;
- 在离开函数前,要把新的数字写入操作数中,是“操作数1”还是“操作数2”?根据当前有没有“运算符”来确定。
【按下运算符[+-*/]时的算法】
- 如果“操作数1”为空,那么给它补一个“0”;
- 如果“操作数1”、“运算符”、“操作数2”均不为空,“结果”为空,则先进行一次计算,然后将结果写入“操作数1”,清空“操作数2”;
- 如果“操作数1”、“运算符”、“操作数2”、“结果”均不为空,则将结果写入“操作数1”,“操作数2”清空;
- 将当前按下的符号按键写入“操作符”;
- 清空“结果”;
- 立flag。
这里的2、3看上去很乱,它们是为了适应按下操作符前各个组件的状态。为什么要有步骤5呢?是为了确保下一次步骤2可以顺利进行。最后看到了吧,立flag。
【按下=时的算法】
- 如果“结果”不为空,则将结果写入“操作数1”;
- 如果“操作数1”为空,那么给它补一个“0”;
- 如果“运算符”不为空且“操作数2”为空,则将“显示屏”上的数字写入“操作数2”;
- 根据当前的“运算符”进行计算,将计算结果保存在“结果中”并显示在“显示屏”上。
3解决了上文中让你晕的那一段中“脑补操作数2”的问题。
+/-只要注意好当前操作的对象就可以了,是哪个操作数,还是结果。就不展开说了。
再提一下%,它比较特殊,有两种计算模式:
- 如果当前只有“操作数1”,则求“操作数1”/100的值,比如按下“5%”结果是“0.05”;
- 如果当前有“操作数1”、“运算符”、“操作数2”,则按下%时,“操作数2”将修改为“操作数1”的百分之“操作数2”,比如按下“5+10%=”,实际完成的计算是“5+5*10%”,操作数二变为0.5,最终的结果是5.5。
就快成功了,基础界面中的功能都已经实现完毕了。可是可是。。。
WTF????
to be continued