java小学生四则运算练习

软件工程导论第一次个人作业!

0.0项目基本任务

使用JAVA编程语言,独立完成一个3到5个运算符的四则运算练习的软件。
软件基本功能要求如下:

• 程序可接收一个输入参数n,然后随机产生n道加减乘除(分别使用符号±÷来表示)练习题,每个数字在 0 和 100 之间,运算符在3个到5个之间。
• 每个练习题至少要包含2种运算符。同时,由于小学生没有分数与负数的概念,你所出的练习题在运算过程中不得出现负数与非整数,比如不能出 3÷5+2=2.6,2-5+10=7等算式。
• 练习题生成好后,将你的学号与生成的n道练习题及其对应的正确答案输出到文件“result.txt”中,不要输出额外信息,文件目录与程序目录一致。
• 当程序接收的参数为4时,以下为一个输出文件示例。
2018010203
13+17-1=29
11
15-5=160
3+10+4-16=1
15÷5+3-2=4
软件附加功能要求如下:(请有余力的同学完成)
• 支持有括号的运算式,包括出题与求解正确答案。注意,算式中存在的括号数必须大于2对,且不得超过运算符的个数。
• 扩展程序功能支持真分数的出题与运算(只需要涵盖加减法即可),例如:1/6 + 1/8 + 2/3= 23/24。注意在实现本功能时,需支持运算时分数的自动化简,比如 1/2+1/6=2/3,而非4/6,且计算过程中与结果都须为真分数。

1.0我的整体思路和基本模块:
在百度了一些博客之后,我基本确定了思路:
1.用两个数组结合来生成运算式中的数字和运算符,并按照一定格式插入左右括号;
2.运用逆波兰算法将中缀表达式转换为后缀表达式,并使用栈对后缀表达式进行计算,并在运算过程中判断排除负数和小数中间结果的出现;
3.最后在Main函数中将结果输出到指定文件;

2.0我的学习和开发过程:
阶段一:
首先我参考了这篇博客,开始大概懂了几个方法后开始编码,还对这篇的方法和整体思路特别有自信,觉得弄懂他的方法就完成了,后来发现是我太天真了,在测试的时候有很多问题。在一边编码的时候由于没有从总体上理解清楚后再下手,使得开发效率低,花多时间调试,浪费很多时间,表达式生成后运算符和括号位置混乱并且没有结果,最后决定另寻他路。
(https://www.cnblogs.com/Fuenli/p/8605497.html)
阶段二:
由于自身java基础很差,我花了很多时间研究别人的博客,而且第一阶段的开发又浪费很时间,所以这时距离交作业很近了,我就想赶紧再找一个思路相近的再学习学习,于是根据上课时老师的推荐找到了这个学姐的代码:(https://coding.net/u/wanghz499/p/2016012032week2-2/git/tree/master/src)
Alt
其中我没有实现"ProperFraction()",其余思路类似:

很巧妙地产生运算式:

//相当于固定了运算式的运算符位置,即格式固定
     switch (operatorCount){
            case 3:{
                if(choose==0){
                    s=num[0]+operator[index[0]]+"("+"("+num[1]+operator[index[1]]+num[2]+")"+operator[index[2]]+num[3]+")";//1+((2×3)-4)型
                }else s="("+num[0]+operator[index[0]]+num[1]+")"+operator[index[1]]+"("+num[2]+operator[index[2]]+num[3]+")";//(1+2)×(3+4)型
                break;
            }

            case 4:{
                if(choose==0){
                    s="("+num[0]+operator[index[0]]+num[1]+")"+operator[index[1]]+num[4]+operator[index[3]]+"("+num[2]+operator[index[2]]+num[3]+")";//(1+2)×3÷(4-1)型
                }else s=num[4]+operator[index[3]]+"("+num[0]+operator[index[0]]+num[1]+")"+operator[index[1]]+"("+num[2]+operator[index[2]]+num[3]+")";//3×(1+2)+(4÷2)型
                break;
            }

            case 5:{
                if(choose==0){
                    s="("+num[0]+operator[index[0]]+num[1]+operator[index[4]]+num[5]+")"+operator[index[1]]+"("+num[4]+operator[index[3]]+num[2]+")"+operator[index[2]]+num[3];//(6+2×3)-(1+2)×3型
                }else s="("+num[0]+operator[index[0]]+"("+num[1]+operator[index[1]]+num[2]+operator[index[2]]+num[3]+")"+")"+operator[index[3]]+"("+num[4]+operator[index[4]]+num[5]+")";//(1+(2×3+4))-(6÷3)型
                break;
            }
        }

        

并且 保证式子里至少有2个不同操作符的方法:

 private static int[] index(int n,int m){ //产生操作符的下标数组
        Random random = new Random();
        int similar=0;
        int[] a = new int[n];
        for(int j=0;j<n;j++){
            a[j] = random.nextInt(m);
        }
        for(int j=1;j<n;j++){
            if(a[0]==a[j]) similar++;
        }
        if(similar==n-1) return index(n); //保证一个式子里至少有2个不同的操作符,若所有操作符下标都一样,则重新产生操作符下标
        else {
            return a;
        }

    }
        

虽然都是核心使用逆波兰表达式,但这个是在中缀转后缀的同时进行计算,
对负数和小数的判断:
(1)在计算时由最基本的运算单元返回的结果来实现,若不符合标准就(return -1;),


 case('-'):{
    res = a - b; //产生负数就不合格
     break;
	}
case('÷'):{
    if(b==0)
		 return -1;
    else if(a%b!=0) //产生小数就不合格
	 	 return -2;
	  else
		res = a / b;
	break;
		    		
	}
        

(2)这样在运算的方法中只要得到了小于0的返回值也返回状态值为-1,

int a = stack1.pop();
int b = stack1.pop();
int sresulat =calculate (b, a, stmp);
  if(sresulat<0)
    return  -1;
 stack1.push(sresulat);
        

(3)在生成运算式的方法中用一个if语句判断此时的结果,若为负数则递归调用生成函数一直到合格为止。

		Calculator c = new Calculator();
        int sum = c.Cal(s);
        s += sum;
        if(sum>=0){ //判断式子是否符合要求,凡是返回负数的就是不合格的
            s+=sum;
        }else {
            return format(); //递归直到产生合格的式子
        }

        return s;
        

后来我又觉得既然已经弄懂了她的想法,想自己改进一下,就又搜了搜中缀转后缀这里,然后找到了这篇让我感觉这个理解起来很透彻:
(https://www.cnblogs.com/fuck1/p/5995857.html)
理解清楚后自己练习一下,在改了很久以后发现这个方法只能处理像(2*3+5=?)这种只含有一位数字的运算,这里我也花了不少时间

	Pattern pattern = Pattern.compile("\\d+||(\\d+\\.\\d+)");
       // 将后缀表达式分割成字符串数组,此处直接使用空白也可以对字符串进行分割!!
   String[] strings = exp.split("");
        

主要问题是我发现这里的 split(" ") 会将 23 这样的数分割成 2 和 3,接下来就无法计算了;学习了用pattern 正则匹配的方法,我尝试着百度split()函数怎样将运算式中的运算符和数字分开,应该是知识储备不够,这个问题没有解决。。。

阶段三:
这时我想再好好研究一下第一次放弃的那个方法,再试试吧;
最终的结果是我没有找到合适的方法排除小数和负数,当式子中没有出现"-" ," ÷"时计算是正确的,希望以后可以改进出来吧,多多学习和练习。

学习到的小知识,也还算有收获:
1.正则表达式:http://www.runoob.com/java/java-regular-expressions.html
2.输出流:https://www.cnblogs.com/fnz0/p/5423201.html
3.类型转换:https://blog.youkuaiyun.com/u010502101/article/details/79162587
4.包括命令行cmd的使用也基本get了

个人总结
首先由于知识和编程经验不足,整个作业效率很低,而且大多数都是看别人的思路后一点点学习的,java学习基本空白,练习的又少;另一方面,我在真正去做的时候也有偷懒的心理,有的时候知其然而不知其所以然,项目整体构思不够清楚,到写的时候要花很多时间,然而编写完其实只是完成了20%的工作量,甚至是无用功,测试和修改的时间真的是比想象的多很多,要解决太多的问题了,因此以后开发时我会先分析清楚再下手;对于以后的学习我觉得还是要采用项目驱动式,一边做一边学,更有针对性,实践出真知!!!

*主要花费的时间如下
**
任务内容 …… 计划与实际
计划 : 10 --------》 20
开发 : 685--------》 2238(~35h)
具体设计 : 40 --------》 100
具体编码 : 6
60 --------》 (7+7+5)*60=1140
测试与修改: 200--------》 420+300=720

谢谢观看~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值