面向对象程序设计课第四次作业

面向对象程序设计课第四次作业

经过多次的查找、参考和修改,终于大致完成了一个(可能)没有 bug 的版本。

初始

根据要求,新建了一个 Calculation 类用以计算。我参考的教程中提到了中缀表示法(Infix expression)、前缀表示法(Prefix expression)和后缀表示法(Postfix expression),这里我用中缀表示法来计算。这种算法的基本思想是一边将数字和运算符分别存储到两个栈,一边根据四则运算法则,判断各个运算符的优先级关系,根据优先级取出运算符和数字进行计算,再将结果压入栈,如此往复,数字栈剩下的最后一个数字即为结果。原教程中用了自己的方法区分运算符和数字,但上次作业使我们获得了已经区分好了的运算符和数字的队列,我们只需简单判断字符串长度是否为 1 就可以进行操作了。参考教程中的函数,注意把运算过程中的 int 类型数据改为 double 类型,而将队列中 string 类型的数字转换为 double 类型,题目中已经提到了使用 <sstream>,在网上找一下相关的使用方法即可。

使用命令行调用程序之前似乎在哪里看到过,将原来的 int main() 改为 int main(int argc, char** argv) 即可,其中 argcargv 是两个固定的参数,argc 表示参数的个数,argv 是一个数组,保存每个参数。因此,若有参数 -a,只需判断 argv[1] 是否为 -a,再进行相应操作。

大概完成后,1+1(3+4)*(34/3) 这类简单的问题都可以解决,但示例 -10000+20-3*(20+2) 会报错,橘子大神告诉我,表达式第一个字符为运算符负号,进行到此处时缺少一个数字进行计算,只需在待运算的数字栈中压入一个 0 即可解决。

难点

解决了 -10000+20-3*(20+2) 问题后,似乎较为复杂的表达式都可以处理了。直到我看到昭锡博客里的测试数据:-(12-(15+6/100-(14*8)))-96/3+36*(12+3-(14-12))-96-(12+3/2-63*15-6+(12-5)),尝试输入后正常得出答案,但我随意在一个左括号后加一个负号,即例如:-96-(-12+3/2-63*15-6+(12-5)),就无法计算了。经过排查,原因是 -(-*(- 这样的部分无法识别,因此若左括号后的第一个数是负数,不能输出一个负号和一个正数,而应将这个负号与数字合并。简单的解决思路是,当遇到左括号和一个负号,输出左括号,跳过负号,将接下来的数字变为相反数。但由于我原来的实现方式是,通过 Scan 类中的函数每次返回一个运算符或数字字符串,在 main 函数中组成这个队列,因此,当我输出左括号时,没办法保存一个用来标记接下来”跳过负号,将接下来的数字变为相反数”操作的 bool 值。所以,为了解决这个负数问题,我只好修改 main 函数,并重写整个 Scan 类中的函数,使它返回一个队列。这其中遇到的问题数不胜数,例如,临时存储字符串的变量在压入队列后需要置空;当前位置、前一个位置、接下来位置的纠葛;若遇到当前字符为负号,前一个字符为左括号的情况,则应该将 bool 值置反,而不是赋值为真,这样可以解决多重括号负号嵌套的情况,如:-(-(-3));# 应该在结束时 push 入队列,否则直接结束运算了;以及大量的逻辑/智商问题。

此外,在看到昭锡文章的更新后,我也注意到若 0 为除数应当报错。

解决

如上所说,我(艰苦地)重写了 Scan 类中的函数,以解决负数的问题,同时参考了网上的文章,利用 <float.h> 中的 _finite(double x) 函数判断结果是否无穷,若是则报错,另外,我顺便支持了 (1+2)(3/4) 这样括号中间省略乘号的格式。

Github 链接

https://github.com/ladit/object-oriented/tree/master/Calculator

参考链接

posted @ 2016-04-15 01:21  Ladit  阅读(140)  评论(3编辑  收藏  举报