结对编程——paperOne基于java的四则运算 功能改进
由于新的需求,原本使用JSP的实现方式目前改为Java实现,即去除了B/S端。
需求分析:
1.四则运算要满足整数运算、分数运算两种;
2.运算题目随机,并且可以打印题目和答案;
3.可以由用户输入答案,并进行对错判断;
4.支持括号。
5.出现的分数要约分,并且以“m p/n”方式输出。
程序概要:
1.原本用JSP实现,为满足需求现改用Java实现;
2.用户可选择题目数量、题目难度(比如简单难度为两位数加减乘除,中等难度为四位数的加减乘除并包含括号,高难度为四位分数加减乘除等);
3.用户可以选择进入答题还是打印问题与答案;
4.答题时输入答案并回车会立即反馈结果;
5.整数运算中不仅运算数是整数,结果也一定是整数;
6.分数运算结果是约分后的,因此填写的答案也需要约分,否则会判为错。
程序运行流程图:

实现过程:
分数类
分数类构造函数
在构造时直接进行约分操作,其中的maxCommonFactor是分子与分母的最大公约数。
public Fraction(int up, int down) {
if (down == 0 | up == 0) {
System.out.println("divided by zero error");
return;
}
int smaller = up > down ? up : down;
int maxCommonFactor = 1;
for (int i = 1; i <= smaller; i++) {
if (up % i == 0 && down % i == 0) {
maxCommonFactor = i;
}
}
this.up = up / maxCommonFactor;
this.down = down / maxCommonFactor;
}
约分函数
与构造函数中的月份操作有所不同,主要用来对进行四则运算后的结果数进行约分操作。
public Fraction gcd(Fraction f) {
int smaller = f.up > f.down ? f.up : f.down;
int maxCommonFactor = 1;
for (int i = 1; i <= smaller; i++) {
if (f.up % i == 0 && f.down % i == 0) {
maxCommonFactor = i;
}
}
f.up = f.up / maxCommonFactor;
f.down = f.down / maxCommonFactor;
return f;
}
转化函数
用于最后打印时使分数符合规范。
public String toString() {
if (down == 1)
return "" + up;
if(Math.abs(up)/down>0){
return up>0?up/down+" "+up%down+"/"+down:"-"+Math.abs(up)/down+" "+Math.abs(up)%down+"/"+down;
}
return up + "/" + down;
}
四则运算函数
因为数据结构用到了栈,所以减法有些特殊,详情后面会给出解释说明,这里的changesign函数就是用来做减法运算的一个中间函数。
public Fraction add(Fraction f) {
Fraction a = new Fraction(up, down);
a.up = f.up * a.down + a.up * f.down;
a.down = a.down * f.down;
return a.gcd(a);
}
public Fraction minus(Fraction f) {
Fraction a = new Fraction(up, down);
a.up = a.up * f.down - f.up * a.down;
a.down = a.down * f.down;
return a.gcd(a);
}
public Fraction multiply(Fraction f) {
Fraction a = new Fraction(up, down);
a.up = a.up * f.up;
a.down = a.down * f.down;
return a.gcd(a);
}
public Fraction divide(Fraction f) {
Fraction a = new Fraction(up, down);
a.up = a.up * f.down;
a.down = a.down * f.up;
return a.gcd(a);
}
public Fraction changeSign(){
up = -up;
return this;
}
随机数函数
用来随机生成分数。
public static Fraction getRandiom(int Max) {
return new Fraction((int) (Math.random() * Max / 2) + 1, (int) (Math.random() * Max / 2) + 1);
}
public static Fraction getRandiom(int Max, boolean isInt) {
return new Fraction((int) (Math.random() * Max / 2) + 1, isInt ? 1 : (int) (Math.random() * Max / 2) + 1);
}
题目生成
这里包含了括号的随机插入:用ArrayList维护随机生成的括号位置列表,然后使用TreeMap计算并保存应该生成括号的位置,最后在题目生成的时候进行拼接。
public String generateQuestion(int numOfOperand, int rangeMin, int rangMax, SupportedOperation[] operation,
boolean isFractional, boolean hasbracket) {
String question = "";
int[] ioperands = null;
ArrayList<Fraction> af = new ArrayList<Fraction>();
SupportedOperation[] so = null;
if (numOfOperand < 2) {
System.out.println("操作数数量至少为2");
return "";
}
if (rangMax > 500) {
System.out.println("操作数数最大值不能超过500");
return "";
}
getBcPrint(numOfOperand);
if (!isFractional) {
ScriptEngine se = new ScriptEngineManager().getEngineByName("JavaScript");
ioperands = new int[numOfOperand];
for (int i = 0; i < numOfOperand; i++) {
ioperands[i] = (int) (Math.random() * rangMax / 2 + 1); }
so = new SupportedOperation[numOfOperand - 1];
for (int i = 0; i < operation.length; i++) {
if (operation[i] == SupportedOperation.ALL) {
operation = new SupportedOperation[4];
operation[0] = SupportedOperation.ADD;
operation[1] = SupportedOperation.MINUS;
operation[2] = SupportedOperation.MULTIPLY;
operation[3] = SupportedOperation.DIVIDE; }
}
// 除法運算,保证整除
int value = 0;
for (int j = numOfOperand - 1; j > 0; j--) {
so[numOfOperand - 1 - j] = operation[(int) (Math.random() * operation.length)];
}
for (int j = numOfOperand - 2; j >= 0; j--) {
if (so[j] == SupportedOperation.DIVIDE) {
if (value < 1) {
ioperands[j] = ioperands[j] * ioperands[j + 1];
value++; } else {
so[j] = operation[(int) (Math.random() * (operation.length - 2))];
}
}
}
// 输出括号
for (int i = 0; i < numOfOperand - 1; i++) {
if (frequency.containsKey(i)) {
if (direction.get(i) == 0) {
for (int k = 0; k < frequency.get(i); k++) {
question += "(";
}
}
}
question += ioperands[i];
if (frequency.containsKey(i)) {
if (direction.get(i) == 1) {
for (int k = 0; k < frequency.get(i); k++) {
question += ")";
}
}
}
question += so[i];
}
if (frequency.containsKey(numOfOperand - 1)) {
if (direction.get(numOfOperand - 1) == 0) {
for (int k = 0; k < frequency.get(numOfOperand - 1); k++) {
question += "(";
}
}
}
question += ioperands[numOfOperand - 1];
if (frequency.containsKey(numOfOperand - 1)) {
if (direction.get(numOfOperand - 1) == 1) {
for (int k = 0; k < frequency.get(numOfOperand - 1); k++) {
question += ")";
}
}
} try {
Integer d = (Integer) se.eval(question);
answer = "" + d;
} catch (Exception e) {
generateQuestion(numOfOperand, rangeMin, rangMax, operation, isFractional, hasbracket);
} } else {
for (int i = 0; i < numOfOperand; i++) {
af.add(Fraction.getRandiom(rangMax));
} so = new SupportedOperation[numOfOperand - 1];
for (int i = 0; i < operation.length; i++) {
if (operation[i] == SupportedOperation.ALL) {
operation = new SupportedOperation[4];
operation[0] = SupportedOperation.ADD;
operation[1] = SupportedOperation.MINUS;
operation[2] = SupportedOperation.MULTIPLY;
operation[3] = SupportedOperation.DIVIDE; }
}
question += af.get(0);
for (int j = 0; j < numOfOperand - 1; j++) {
so[j] = operation[(int) (Math.random() * operation.length)];
question += (so[j] == SupportedOperation.DIVIDE ? "÷" : so[j].toString()) + af.get(j + 1); }
answer = getanswer(af, so).toString();
try {
} catch (Exception e) {
e.printStackTrace();
}
} return question; }
}
括号维护
public String answer;
Stack<SupportedOperation> oplist = new Stack<SupportedOperation>();
Stack<Fraction> numlist = new Stack<Fraction>();
ArrayList<Integer[]> bclist;
TreeMap<Integer, Integer> frequency;
TreeMap<Integer, Integer> direction;
private void getBcPrint(int numOfOperand) {
bclist = new ArrayList<Integer[]>();
if (numOfOperand > 2) {
int bcnum = (int) (Math.random() * (numOfOperand - 2));
for (int n = 0; n < bcnum; n++) {
Integer[] bracket = new Integer[2];
bracket[0] = (int) (Math.random() * (numOfOperand - 2));
bracket[1] = (int) (Math.random() * (numOfOperand - 2 - bracket[0]) + bracket[0]);
if (bracket[0] == bracket[1]) {
bracket[1]++;
}
boolean canput = true;
for (int i = 0; i < bclist.size(); i++) {
Integer[] tmp = bclist.get(i);
if (bracket[0] < tmp[0] & bracket[1] >= tmp[0] & bracket[1] < tmp[1]) {
canput = false;
break;
} else if (bracket[1] > tmp[1] & bracket[0] > tmp[0] & bracket[0] <= tmp[1]) {
canput = false;
break;
} else if (bracket[0] == tmp[0] & bracket[1] == tmp[1]) {
}
}
if (canput) {
bclist.add(bracket);
}
}
}
frequency = new TreeMap<Integer, Integer>();
direction = new TreeMap<Integer, Integer>();
for (int i = 0; i < bclist.size(); i++) {
Integer[] tmp = bclist.get(i);
if (frequency.containsKey(tmp[0])) {
frequency.put(tmp[0], frequency.get(tmp[0]) + 1);
} else {
frequency.put(tmp[0], 1);
direction.put(tmp[0], 0);
}
if (frequency.containsKey(tmp[1])) {
frequency.put(tmp[1], frequency.get(tmp[1]) + 1);
} else {
frequency.put(tmp[1], 1);
direction.put(tmp[1], 1);
}
}
}
计算结果
public Fraction getanswer(ArrayList<Fraction> frlist, SupportedOperation[] so) {
numlist.push(frlist.get(0));
for (int n = 0; n < so.length; n++) {
switch (so[n]) {
case ADD:
oplist.push(so[n]);
numlist.push(frlist.get(n + 1));
break;
case MINUS:
oplist.push(SupportedOperation.ADD);
numlist.push(frlist.get(n + 1).changeSign());
break;
case MULTIPLY: {
Fraction r = numlist.pop().multiply(frlist.get(n + 1));
numlist.push(r);
}
break;
case DIVIDE: {
Fraction r = numlist.pop().divide(frlist.get(n + 1));
numlist.push(r);
}
break;
default:
System.out.println("不支持的运算");
break;
}
}
while (!oplist.isEmpty()) {
Fraction answer = numlist.pop();
switch (oplist.pop()) {
case ADD: {
answer = answer.add(numlist.pop());
numlist.push(answer);
}
break;
case MINUS: {
answer = answer.minus(numlist.pop());
numlist.push(answer);
}
break;
default:
System.out.println("不支持的运算");
break;
}
}
return numlist.pop();
}
程序运行结果:
简单难度答题:

普通难度出题打印:

复杂题目出题打印:

程序退出:

结对编程体会
两个人在一台电脑上进行编码,其实感觉效率未必会提高,编码速度取决于正在编程的人的打字速度。而且有时候反而会因为在同一问题上看法不同、解决方式不同而产生分歧。不过因为结对编程可以了解到他人的编程思路,也是一种学习的过程。所以本次体会觉得效率提高是其次,互相学习才是最大的收获!
争论点:
1.整除问题:输出结果在尽量调节为整数的情况下可能因括号的出现导致结果为小数或者分数,对于这样的情况选择直接重出题目(仅限简单和普通难度的问题);
2.分数减法入栈出栈顺序:计算结果采用栈来解决,出栈时仅剩加减法需要运算,当出现减法,甚至连减时选择将减法换为加法运算,然后改变操作数符号,即Fraction.changSign();
3.括号列表维护:括号生成是根据操作数数量决定的,生成括号位置列表时排除无效和冗余括号,最后将括号列表转化位置和括号方向、数量的Map,用于打印括号,即先有操作数后有括号的设计,而不是括号匹配的方式;
4.连除问题:用计数器检测除法的出现,防止连除,但是不会妨碍一个题目出现两个或更多的除法运算(仅限简单和普通难度)。因为在整数运算中,为保证可以整除,需要改变被除数,这样做太多次连除会导致最顶层操作数过大,不利于题目平衡性。
花费时间较长的问题:括号位置列表生成及维护。
工程地址:https://coding.net/u/jx8zjs/p/paperOne/git
SSH: ssh://git@git.coding.net:jx8zjs/paperOne.git
结对编程——paperOne基于java的四则运算 功能改进的更多相关文章
- 结对编程——paperOne基于java web的简易四则运算出题网站
项目成员:张金生 张政 需求分析: 1.要进行四则运算: 2.运算题目随机: 3.进行对错判断: 4.整数运算. 程序概要: 1.用JSP实现: 2.用户可选择题目数量: 3.答题页用表格列出 ...
- 结对编程1----基于java的四则运算生成器
小组成员:王震(201421123054).王杰(201421123055) Coding地址:https://git.coding.net/a506504661/sssss.git 一.题目描述 我 ...
- 20165325 2017-2018-2 《Java程序设计》结对编程_第二周:四则运算
20165325 2017-2018-2 <Java程序设计>结对编程_第二周:四则运算 一.码云链接 FAO项目的码云链接; 1.Git提交日志已经实现一个功能/一个bug修复的注释说明 ...
- 20165325 2017-2018-2 《Java程序设计》结对编程_第一周:四则运算
一.码云链接 项目名称FAO 码云链接 二.需求分析 实现一个命令行程序: 自动生成小学四则运算题目(加.减.乘.除) 支持整数 支持多运算符(比如生成包含100个运算符的题目) 支持真分数 统计正确 ...
- paperOne基于java web的简易四则运算出题网站
项目成员:张金生 张政 需求概要 1.运算数均为正整数 2.包含的运算符有+,-,*,/ 3.除法运算结果为整除运算 4.批量生成题目并判题 核心功能分析 1.题目生成——java后端 题目生 ...
- 结对编程1 —— 基于GUI和Swing的四则运算题目生成器
合作伙伴 201421123102 王艳秋 201421123106 陈 雄 代码地址 题目描述 我们在个人作业1中,用各种语言实现了一个命令行的四则运算小程序.进一步,本次要求把这个程序做成GUI( ...
- 结对作业:基于GUI实现四则运算
1)Coding.Net项目地址:https://git.coding.net/day_light/GUIszysLL.git 2)在开始实现程序之前,在下述PSP表格记录下你估计将在程序的各个模块的 ...
- 结对编程1---基于Flask的四则运算题目生成器
项目代码地址 / WEB应用地址 / 合作伙伴iFurySt博文链接 需求分析 本次程序是基于原有的控制台四则运算器的基础上,改成WEB的形式,同时还增加了一些新的功能.同时因为交互方式的改变,代码也 ...
- 结对编程1-基于GUI的四则运算生成器
201421123016郑怀勇 201421123017康建灿 程序代码 / 康建灿 一.需求分析 记录用户的对错总数. 程序退出再启动的时候,能把以前的对错数量保存并在此基础上增量计算. 有 ...
随机推荐
- 给网卡设备添加两个IP别名(一个网卡绑定多个ip)
首先执行ifconfig,查看网卡设备名称 [root@localhost conf]# ifconfig ens33: flags=4163<UP,BROADCAST,RUNNING,MULT ...
- 使用nexus 管理pip 私有包
nexus 已经支持了对于python pip 包的管理(支持group,host,proxy) 这个是一个简单的使用docker 运行的demo,同时集成了s3 存储,以及 一个为了测试简单的自定义 ...
- Modern Data Lake with Minio : Part 1
转自:https://blog.minio.io/modern-data-lake-with-minio-part-1-716a49499533 Modern data lakes are now b ...
- JSON数据的处理中的特殊字符
JSON现在是很常见的处理数据的方式了.但由于自己使用的是反射获取数据,必须自己处理特殊字符,但总是发现有一些看不见的字符在前台 var obj = jQuery.parseJSON(msg);会转换 ...
- 模拟实现strncpy,strncat,strncmp
1.模拟实现strncpy <1.>strncpy相比于strcpy增加了size_t参数可以实现最多拷贝的字节数<2.>(size_t不可以超出拷贝存放的内存大小)来保证不 ...
- flutter 控制台快捷键
- [转]DB2中需要REORG操作的几种情况
问题: 在DB2数据库中,修改完表的结构时,是否需要对表做一个reorg操作才能使表的状态恢复正常? 答:有以下4种操作,需要对表做reorg操作 1. SET DATA TYPE altered-d ...
- egg-init 知识点
Create a simple type application $ egg-init --type simple [dest]
- vue项目权限控制
Vue权限控制有各种方法,大概分为两个方向: 把当前角色对应的权限保存在浏览器本地(容易被恶意修改): 将操作权限保存在vuex中(推荐此种方式:页面一刷新就没了,可以再次向后端请求相关数据,始终保持 ...
- linux 自总结常用命令(centos系统)
查看apache2的命令 httpd -V 其中HTTPD_ROOT和SERVER_CONFIG_FILE 就可以确定httpd.conf(Apache配置文件)的路径了 apache启动.停止.重 ...