项目成员:张金生     张政

  由于新的需求,原本使用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的四则运算 功能改进的更多相关文章

  1. 结对编程——paperOne基于java web的简易四则运算出题网站

    项目成员:张金生     张政 需求分析: 1.要进行四则运算: 2.运算题目随机: 3.进行对错判断: 4.整数运算. 程序概要: 1.用JSP实现: 2.用户可选择题目数量: 3.答题页用表格列出 ...

  2. 结对编程1----基于java的四则运算生成器

    小组成员:王震(201421123054).王杰(201421123055) Coding地址:https://git.coding.net/a506504661/sssss.git 一.题目描述 我 ...

  3. 20165325 2017-2018-2 《Java程序设计》结对编程_第二周:四则运算

    20165325 2017-2018-2 <Java程序设计>结对编程_第二周:四则运算 一.码云链接 FAO项目的码云链接; 1.Git提交日志已经实现一个功能/一个bug修复的注释说明 ...

  4. 20165325 2017-2018-2 《Java程序设计》结对编程_第一周:四则运算

    一.码云链接 项目名称FAO 码云链接 二.需求分析 实现一个命令行程序: 自动生成小学四则运算题目(加.减.乘.除) 支持整数 支持多运算符(比如生成包含100个运算符的题目) 支持真分数 统计正确 ...

  5. paperOne基于java web的简易四则运算出题网站

    项目成员:张金生     张政 需求概要 1.运算数均为正整数 2.包含的运算符有+,-,*,/ 3.除法运算结果为整除运算 4.批量生成题目并判题 核心功能分析 1.题目生成——java后端 题目生 ...

  6. 结对编程1 —— 基于GUI和Swing的四则运算题目生成器

    合作伙伴 201421123102 王艳秋 201421123106 陈 雄 代码地址 题目描述 我们在个人作业1中,用各种语言实现了一个命令行的四则运算小程序.进一步,本次要求把这个程序做成GUI( ...

  7. 结对作业:基于GUI实现四则运算

    1)Coding.Net项目地址:https://git.coding.net/day_light/GUIszysLL.git 2)在开始实现程序之前,在下述PSP表格记录下你估计将在程序的各个模块的 ...

  8. 结对编程1---基于Flask的四则运算题目生成器

    项目代码地址 / WEB应用地址 / 合作伙伴iFurySt博文链接 需求分析 本次程序是基于原有的控制台四则运算器的基础上,改成WEB的形式,同时还增加了一些新的功能.同时因为交互方式的改变,代码也 ...

  9. 结对编程1-基于GUI的四则运算生成器

    201421123016郑怀勇     201421123017康建灿 程序代码 / 康建灿 一.需求分析 记录用户的对错总数. 程序退出再启动的时候,能把以前的对错数量保存并在此基础上增量计算. 有 ...

随机推荐

  1. Mybatis二(高级部分)

    1.输入映射和输出映射 a)        输入参数映射 b)        返回值映射 2.动态sql a)        If标签 b)        Where标签 c)        Sql片 ...

  2. TECH books

    文章目录 TECH books linux vxworks bat c gdb c++ vbscript make java bash perl web uml software-misc cpu e ...

  3. cookie和session机制

    一.cookie和session机制之间的差别和联系 1.cookie机制 Cookie意为"甜饼",是由W3C组织提出,最早由Netscape社区发展的一种机制. 眼下Cooki ...

  4. S老师 C#编程数据结构篇 学习

    直接插入排序                                                       冒泡排序 简单选择排序 线性表: using System; using Sy ...

  5. css加载字体跨域问题

    刚才碰到一个css加载字体跨域问题,记录一下.站点的动态请求与静态文件请求是不同的域名的.站点的域名为 www.domain.com,而静态文件的域名为 st.domain.com.问题:页面中加载c ...

  6. jmeter---将回应数据写入到文件

    jmeter---将回应数据写入到文件 JMeterPlugins (插件监听器)Flexible File Writer:这个插件允许你灵活记录测试结果 Filename:结果记录的地方 Overw ...

  7. mvc 部署到iis 提示错误未能加载文件或程序集System.Web.Http.WebHost

    Nuget程序包管理 —>程序包管理控制台,运行以下命令即可: Update-Package Microsoft.AspNet.WebApi -reinstall

  8. MAC JDK默认安装路径 JAVA路径

    打开终端,执行     /usr/libexec/java_home -V 默认JDK1.6(Apple自带JDK)路径:   /System/Library/Java/JavaVirtualMach ...

  9. google浏览器查看源码快捷键 ctrl+U

    google浏览器查看源码快捷键 ctrl+U 或则在地址栏的网址前加上:view-source:

  10. NDK学习笔记(Add.cpp注释)(一)

    // Add.C // Copyright (c) 2009 The Foundry Visionmongers Ltd. All Rights Reserved. static const char ...