• 结对编程练习


    结对编程---四则运算

    一、需求分析

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

    1、题目要求:

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

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

    2、题目分析:

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

    3、分工

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

    二、 程序设计

    图0.0为修改前的思维导图

    图0.0

    图0.1为修改前的思维导图

    图0.1


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

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

    如下图所示在老师所给的源代码与博客里面的源代码并不相符。

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

    改进1:主界面的优化。

    图1.1

    图1.2

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

    改进代码如下图所示:

    改进2:增添功能---“统计回答正确的题目的题号”

    图2.1

    图2.2
    改进代码如下图所示;

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

    改进3:计时器功能优化

    图3.1


    图3.2

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

    4、增加随机产生括号

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

    6、避免出题重复的代码

    如下代码为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
    Error: %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);
    	}
    }
    
    

    终结版运行界面


    开始计时,同时给出题目


    下图为我们俩的码云提交记录:

    PSP:

    下图为我和我的同伴挑灯夜战研究写代码的照片:

    三、个人总结

    ◆在本次结对编程实验中,感慨颇大,最初点开题目什么意思都不太理解,经过和同伴共同讨论之后,明白了整体要做什么,接着我们分别着手开始看代码,我们选用老师给的范例中的第二份代码,但是其中的代码结构条理并不是很清晰,我们花了很多时间去看懂他们的代码,然后找到了运行后的一些bug,于是就开始完善和优化之前的代码。后来做到第二题的时候,看到要加入新的运算符——括号,并且不能重复出题,感觉难度特别的,我们每天就一直研究这个,经过不断思考和查找一些资料,终于把这一部分搞定了,实在是觉得千辛万苦,不过做出来之后还是成就感十足的!

  • 相关阅读:
    C# 多线程 异步加载 窗体
    C# 多线程 异步加载 窗体
    C#中的Debug类
    C#中的Debug类
    C# DataGridView:为行头添加行号
    C# DataGridView:为行头添加行号
    向SqlParameter内动态添加参数
    向SqlParameter内动态添加参数
    C# params关键字
    C# params关键字
  • 原文地址:https://www.cnblogs.com/yjliao/p/8645378.html
Copyright © 2020-2023  润新知