软件工程网络15结对编程作业

结对编程---四则运算

一、需求分析

四则运算生成器在当今大家计算能力低下的时代是很有必要存在的,这个小软件能够锻炼一个人的计算能力,并且经过练习能够快速提高能力。

1、题目要求:

我们选用的是老师给出范例中的第二套。
我们在个人作业1中,用各种语言实现了一个命令行的四则运算小程序。进一步,本次要求把这个程序做成GUI(可以是Windows PC 上的,也可以是Mac、Linux,web,手机上的),成为一个有基本功能、一定价值的程序。在下面的功能需求中实现两个:

记录用户的对错总数,程序退出再启动的时候,能把以前的对错数量保存并在此基础上增量计算。
有计时功能,能显示用户开始答题后的消耗时间。
界面支持中文简体/中文繁体/英语,用户可以选择一种;

2、题目分析:

(1)支持真分数和整数的运算;
(2)能够判断对错,且输出正确答案;能够计算正确率并输出;
(3)将程序弄成GUI;
(4)可切换语言,界面支持中文简体/中文繁体/英语,用户可以选择一种;
(5)计时器功能,点击开始计时时,能显示用户开始答题后的消耗时间;

3、分工

此次程序设计由我,齐畅(201521123073)和廖怡洁(201521123067)共同完成。
我的码云地址:https://gitee.com/yjliao/four_operations
同伴博客地址:http://www.cnblogs.com/yjliao/p/8645378.html

二、 程序设计

图0.0为修改前的思维导图
1110002-20180325141713326-663256959.png
图0.0

图0.1为修改前的思维导图
1110002-20180325141721889-1744826116.png
图0.1


我们在此基础上完善修改增添了如下功能:

1、主界面的优化;
2、增添功能---“统计回答正确的题目的题号”
3、计时器功能优化
4、增加随机产生括号
5、增加带括号运算
6、避免出题重复

如下图所示在老师所给的源代码与博客里面的源代码并不相符。
1110002-20180325141658202-1430489280.png
我们先运行了源代码里面的代码,十分简陋,bug很多,还没有界面。
功能设计不合理,比如:如下图所示,居然要手动输入求解的式子。
后来我们点开了个人博客的码云地址,接下来的功能完善均基于此链接。
https://coding.net/u/Belong033/p/java-second-two/git?public=true

1110002-20180325141626303-308505213.png

改进1:主界面的优化。
1110002-20180325141738797-1723310815.png
图1.1
1110002-20180325141745070-1402215151.png
图1.2

图1.1为原来的欢迎界面,图1.2为改进后的界面。字体以及排版有所细微的变化。改进之后,视觉效果更合理。

改进代码如下图所示:

1110002-20180325141753724-821447314.png

改进2:增添功能---“统计回答正确的题目的题号”
1110002-20180325141823907-404835735.png
图2.1
1110002-20180325141830267-1844464540.png
图2.2
改进代码如下图所示;
1110002-20180325141840945-1101667281.png

随后我们又对“正确题号”那一栏的大小排版进行了相对应的设置,改进代码如下图所示;

1110002-20180325141847438-1382094353.png

改进3:计时器功能优化
1110002-20180325141856167-220568375.png
图3.1

1110002-20180325141902557-2028706449.png
图3.2

原设计思路是,要手动点击“计时开始”才开始计时,如图3.1所示。
不过很明显,这种方式及其容易作弊。因为如图3.1所示,计时器并没有及时,但是答题已经结束啦!
即:考生不手动点击计时器,计时器就永远不会开始计时。
其实并不是计时器本身有错误,而是设计不够缜密,有思维漏洞,因此我和队友就这个漏洞予以弥补。
如图3.2所示,只有点击“计时开始”系统才会自动跳出题目。因此,改进之后更合理,更公平。
同时我们分别对简体、繁体、英文也相应做出了优化功能。


改进代码如下图所示:

1110002-20180325141913621-1469965784.png

4、增加随机产生括号

1110002-20180325141921196-978066888.png

5、增加带括号运算的代码

1110002-20180325141927113-1967218571.png

6、避免出题重复的代码

1110002-20180325141946026-1324046931.png

如下代码为Calculator类,代码里面有详细备注

package zong;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

public class Calculator {

    public static String str1;
    public static double answ;

    public double getAnsw() {
        return answ;
    }

    public void setAnsw(double answ) {
        this.answ = answ;
    }

    public String getStr1() {
        return str1;
    }

    public void setStr1(String str1) {
        this.str1 = str1;
    }

    private final Stack<Double> numStack = new Stack<Double>();
    private final Stack<Character> opStack = new Stack<Character>();

    private char currentOperator;
    private char opStackTop;

    private int i;
    private String expression;

    @SuppressWarnings("rawtypes")
    public void exec(String expression) {
        try {
            clean();
            if (expression == null || expression.isEmpty()) {
                throw new IllegalArgumentException("Blank Expression!");
            }
            this.expression = expression;
            opStack.push(TERMINATE_TOKENS.START_END_MARK);
            List tokens = TOKENIZER.exec(expression
                    + TERMINATE_TOKENS.START_END_MARK);
            for (; i < tokens.size(); i++) {
                final Object token = tokens.get(i);
                if (token instanceof Double) {
                    processOperand((double) token);
                } else {
                    processOperator((char) token);
                }
            }
        } catch (Throwable e) {
            System.err.println(String.format(
                    "Incorret Expression: %s\nError: %s", expression,
                    e.getMessage()));
        }
    }

    private void processOperand(final double operand) {
        numStack.push(operand);
    }

    private void processOperator(final char currentOperator) {
        this.currentOperator = currentOperator;
        this.opStackTop = opStack.peek();
        char calMode = CALCULATE_MODE.getRule(currentOperator, opStackTop);
        switch (calMode) {
        case '>':
            processStackHigerPriorityOperator();
            break;
        case '<':
            processStackLowerPriorityOperator();
            break;
        case '=':
            processStackEqualPriorityOperator();
            break;
        default:
            break;
        }
    }

    private void processStackLowerPriorityOperator() {
        opStack.push(currentOperator);
    }

    private void processStackHigerPriorityOperator() {
        numStack.push(CALCULATE.exec(opStack.pop(), numStack.pop(),
                numStack.pop()));
        --i; // pointer back to the previous operator.
    }

    private void processStackEqualPriorityOperator() {
        if (TERMINATE_TOKENS.START_END_MARK == currentOperator) {
            // float answ = (float) (Math.round(numStack.peek() * 10)) / 10;//
            setAnsw((Math.round(numStack.peek() * 10)) / 10);// 对结果保留一位小数,如果保留两位,就把10改成100
            System.out.println(expression + " = " + numStack.peek());
        } else if (')' == currentOperator) {
            opStack.pop();
        }
    }

    public void clean() {
        numStack.clear();
        opStack.clear();
        i = 0;
    }

    public static void main(String[] args) {
        Calculator cal = new Calculator();

        try {
            int op;// 随机选择计算符
            int kuo;// 是否在此处加入括号

            int[] number = new int[100];
            for (int i = 1; i <= 7; i++) {
                // XXD:需要多少个数字就修改这里!!!
                number[i] = (int) (Math.random() * 9 + 1);
            }

            int flag = 0;// 是否已经有左括号
            int flag2 = 0;// 左右括号之间数字的个数
            int cnt = 1;// 已经使用的数字的个数
            String str = "";// 储存准备用于计算的运算串
            String fuhao;// 储存随机产生的符号

            while (true) {

                kuo = (int) (Math.random() * 2 + 1);// 是否在此处加入括号
                if (kuo == 1 && flag == 0 && cnt != 7) {
                    str += "(";
                    flag = 1;
                    flag2 = 0;
                }

                str += Integer.toString(number[cnt]);
                cnt++;

                op = (int) (Math.random() * 4 + 1);// 随机选择计算符
                if (op == 1) {
                    fuhao = "+";
                } else if (op == 2) {
                    fuhao = "-";
                } else if (op == 3) {
                    fuhao = "*";
                } else {
                    fuhao = "/";
                }

                if (cnt == 8) {// 数字个数上限+1
                    if (flag == 1)
                        str += ")";
                    break;
                }

                kuo = (int) (Math.random() * 2 + 1);
                flag2++;

                if (kuo == 1 && flag == 1 && flag2 == 2) {
                                                // 随机决定此处加右括号 &&
                                                // 已经有左括号
                                                // && 左右括号之间最少有两个数
                    str += ")";
                    flag = 0;
                    flag2 = 0;
                }
                str += fuhao;
            }

            // cal.exec("4+(3*(3-1)+2)/2"); //测试 = 8.0
            // cal.exec("4+(-3*(3-1)+2)"); //测试 = 0.0
            cal.exec(str);
            cal.setStr1(str);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

enum CALCULATE {
    INSTANCE;

    public static double exec(final char operator, final double right,
            final double left) {
        switch (operator) {
        case '+':
            return left + right;
        case '-':
            return left - right;
        case '*':
            return left * right;
        case '/':
            return left / right;
        default:
            throw new IllegalArgumentException("Unsupported operator: "
                    + operator);
        }
    }
}

enum TERMINATE_TOKENS {
    INSTANCE;

    public static final char START_END_MARK = '#';
    private static final Map<Character, Integer> TOKENs = new HashMap<Character, Integer>();

    static {
        // token, token id
        TOKENs.put('+', 0);
        TOKENs.put('-', 1);
        TOKENs.put('*', 2);
        TOKENs.put('/', 3);
        TOKENs.put('(', 4);
        TOKENs.put(')', 5);
        TOKENs.put(START_END_MARK, 6);
    }

    private static Set<Character> NEGATIVE_NUM_SENSITIVE = new HashSet<Character>();

    public static synchronized Set<Character> getNegativeNumSensitiveToken() {
        if (NEGATIVE_NUM_SENSITIVE.size() == 0) {
            NEGATIVE_NUM_SENSITIVE.addAll(TOKENs.keySet());
            NEGATIVE_NUM_SENSITIVE.remove(')');
        }
        return NEGATIVE_NUM_SENSITIVE;
    }

    public static boolean isTerminateToken(final char token) {
        Set<Character> keys = TOKENs.keySet();
        return keys.contains(token);
    }

    public static int getTokenId(final char token) {
        return TOKENs.get(token) == null ? -1 : TOKENs.get(token);
    }

    public static int getTokenSize() {
        return TOKENs.size();
    }

}

enum CALCULATE_MODE {
    INSTANCE;

    private static char[][] RULES = {
            // + - * / ( ) #
            { '>', '>', '<', '<', '<', '>', '>' }, // +
            { '>', '>', '<', '<', '<', '>', '>' }, // -
            { '>', '>', '>', '>', '<', '>', '>' }, // *
            { '>', '>', '>', '>', '<', '>', '>' }, // /
            { '<', '<', '<', '<', '<', '=', 'o' }, // (
            { '>', '>', '>', '>', 'o', '>', '>' }, // )
            { '<', '<', '<', '<', '<', 'o', '=' }, // #
    };

    static {
        if (RULES.length != TERMINATE_TOKENS.getTokenSize() || RULES.length < 1
                || RULES[0].length != TERMINATE_TOKENS.getTokenSize()) {
            throw new IllegalArgumentException("Rules matrix is incorrect!");
        }
    }

    public static char getRule(final char currentOperator, final char opStackTop) {
        try {
            return RULES[TERMINATE_TOKENS.getTokenId(opStackTop)][TERMINATE_TOKENS
                    .getTokenId(currentOperator)];
        } catch (Throwable e) {
            throw new RuntimeException("No rules were defined for some token!");
        }
    }
}

enum TOKENIZER {
    INSTANCE;

    private static final StringBuilder BUFFER = new StringBuilder();

    private static String clearExpression(String expression) {
        return expression.replaceAll(" ", "");
    }

    private static Character PREVIOUS_CHAR;

    private static void clean() {
        BUFFER.delete(0, BUFFER.length());
        PREVIOUS_CHAR = null;
    }

    private static boolean processNegativeNumbers(final String exp,
            final int index) {
        char c = exp.charAt(index);
        if (('+' == c || '-' == c)
                && (PREVIOUS_CHAR == null || TERMINATE_TOKENS
                        .getNegativeNumSensitiveToken().contains(PREVIOUS_CHAR))
                && !TERMINATE_TOKENS.isTerminateToken(exp.charAt(index + 1))) {
            BUFFER.append(c);
            return true;
        }
        return false;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static List<?> exec(final String expression) {
        clean();
        String exp = clearExpression(expression);
        List result = new LinkedList();
        for (int i = 0; i < exp.length(); i++) {
            char c = exp.charAt(i);
            if (TERMINATE_TOKENS.isTerminateToken(c)) {
                if (processNegativeNumbers(exp, i))
                    continue;
                if (BUFFER.length() > 0) {
                    result.add(Double.valueOf(BUFFER.toString()));
                    BUFFER.delete(0, BUFFER.length());
                }
                result.add(c);
            } else {
                BUFFER.append(c);
            }
            PREVIOUS_CHAR = c;
        }
        return Collections.unmodifiableList(result);
    }
}

终结版运行界面

1110002-20180325142010135-547197739.png
1110002-20180325142021249-1630487023.png

开始计时,同时给出题目

1110002-20180325142034699-1329196105.png
1110002-20180325142041833-160978434.png

下图为我们俩的码云提交记录:
1109970-20180325172213295-1782620730.png

1109970-20180325172251216-157903322.png


PSP:

1109970-20180325172152093-973079853.png

这次所有的代码都是我们俩一起完成的,连续几天熬夜打代码,虽然过程中遇到超级多的问题,但是也都最终想办法解决了。
比如最后一个“括号‘的那个功能,搞了好久也运行不了,思路当时没觉得有什么问题,本来我们都要放弃了,但是一想到这毕竟是团队合作,一定要尽自己最大努力呀!不能放弃的呀!
后来厚脸皮请教无数人,改了好多纰漏,终于成功运行!!!也发现我们的思维确实还是不够严密。但也因此更加痴迷于团队合作的魅力。
结对编程让我收获很多,也意识到自己的不足,主动的找方法去解决、去学习。也让自己的编程能力有很大的提升,培养了耐心和细心。
刚开始还有小小的抵触,不过,直到现在提交博客的一瞬间才真正感悟结对编程的意义,感谢结对编程!
至此,附上一张我跟我队友凌晨02:00挑灯夜战的感人画面。


1110002-20180325143030128-131008976.png

转载于:https://www.cnblogs.com/qichang/p/8644461.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值