软件工程(2018)结对编程第二次作业

  • 我们在刚开始上课的时候介绍过一个小学四则运算自动生成程序的例子,请实现它,要求:
    • 能够自动生成四则运算练习题
    • 可以定制题目数量
    • 用户可以选择运算符
    • 用户设置最大数(如十以内、百以内等)
    • 用户选择是否有括号、是否有小数
    • 用户选择输出方式(如输出到文件、打印机等)
    • 最好能提供图形用户界面(根据自己能力选做,以完成上述功能为主)

二.角色分工

驾驶员:杨浩(本人)
领航员:冯俊鹏 博客地址

三.代码及代码地址

代码地址

题目生成

class BinaryTree 
{
	
	private TreeNode root;
	private int num;
	private ArrayList<TreeNode> opeList = new ArrayList<TreeNode>();
	
	public BinaryTree(int num){
		this.num=num;
	}
	
	public int getNum(){
		return num;
	}
	
	public void setNum(int num){
		this.num = num;
	}
	
	public void setTreeNode(TreeNode root){
		this.root = root;
	}
	
	
	/**
	 * 获取最终的表达式,必须在CalAndVal()方法后调用
	 * 
	 * @return str
	 */
	public String toString(){
		String str = root.toString();
		str = str.substring(1, str.length()-1);
		return str;
	}
	
	/**
	 * 计算并验证表达式
	 * 
	 * @return result
	 */
	public String CalAndVal(){
		return root.getResult();
	}
	
	/**
	 * 计算二叉树的深度(层数) 
	 * 
	 * @return deep
	 */
	public int getDeep(){
		int i = this.num;
		int deep = 2;
		while(i/2 > 0){
			deep++;
			i /= 2;
		}
		return deep;
	}
	
	/**
	 * 生成二叉树
	 * 
	 */
	public void createBTree(){
		TreeNode lchild, rchild, lnode, rnode;
		
		if(num == 1){
			lchild = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null);
			rchild = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null);
			root = new TreeNode(String.valueOf(Ran.getOperator()), lchild, rchild);
		}
		else{
			int num1 = 0;
			int n = getDeep() - 3;
			boolean[] place = Ran.getChildPlace(num);
			root = new TreeNode(String.valueOf(Ran.getOperator()), null, null);
			opeList.add(root);
			
			for(int i = 0; i < n; i++){
				for(int j = 0; j < (int)Math.pow(2, i); j++, num1++){
					lchild = new TreeNode(String.valueOf(Ran.getOperator()), null, null);
					rchild = new TreeNode(String.valueOf(Ran.getOperator()), null, null);
					opeList.get(j + num1).setChild(lchild, rchild);  //?
					opeList.add(lchild);
					opeList.add(rchild);
				}
			}
			
			for(int i = 0; i < place.length; i++){
				if(place[i]){
					lnode  = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null);
					rnode  = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null);
					if(i%2 == 0){
						lchild = new TreeNode(String.valueOf(Ran.getOperator()), lnode, rnode);
						opeList.add(lchild);
						opeList.get(num1).setLchild(lchild);
					}
					else{
						rchild = new TreeNode(String.valueOf(Ran.getOperator()), lnode, rnode);
						opeList.add(rchild);
						opeList.get(num1).setRchild(rchild);
					}
				}
				else{
					if(i%2 == 0){
						lchild = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null);
						opeList.get(num1).setLchild(lchild);
					}
					else{
						
						rchild = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null);
						opeList.get(num1).setRchild(rchild);
					}
				}
				num1 = num1 + i%2;
			}
		}
	}
}

 class Ran {
	
	/**
	 * 获取随机的符号
	 * 
	 * @return operator
	 */
	
	public static char getOperator(){
		char operator = 0;
		Random ran = new Random();
		int i = ran.nextInt(4);
		switch(i){
			case 0:
				operator = '+';
				break;
			case 1:
				operator = '-';
				break;
			case 2:
				operator = '*';
				break;
			case 3:
				operator = '/';
				break;
		}
		return operator;
	}
	
	
	/**
	 * 根据输入的范围获取随机数
	 * 
	 * @param max
	 * @return number
	 */
	
	public static int getNumber(int max){
		int number = 0;
		max=test.maxNum;
		
			Random ran = new Random();
			number = ran.nextInt(max+1);
			return number;
	}
	
	/**
	 * 根据运算符的个数随机产生子节点的位置
	 * 
	 * @param num
	 * @return childPlace
	 */
	
	public static boolean[] getChildPlace(int num){
		int d = 0;
		int size = 0, j=1;
		while(num >= (int)Math.pow(2, j)){
			j++;
		}
		d = (int)Math.pow(2, j) - 1 - num;
		size = (int)Math.pow(2, j-1);
		boolean[] k = new boolean[size];
		for(int i = 0; i < size; i++){
			k[i] = true;
		}
		for(int i = 0; i < d; i++){
			Random ran = new Random();
			int f = ran.nextInt(size);
			while(k[f] == false)
			{
				f = ran.nextInt(size);
			}
			k[f] = false;
		}
		return k;
	}
}

 class TreeNode {
	
	private String str;
	private TreeNode rchild = null;
	private TreeNode lchild = null;
	
	public TreeNode(String str){
		this.str = str;
	}
	
	public TreeNode(String str, TreeNode lchild, TreeNode rchild){
		this.str = str;
		this.rchild = rchild;
		this.lchild = lchild;
	}
	
	public void setChild(TreeNode lchild, TreeNode rchild){
		this.lchild = lchild;
		this.rchild = rchild;
	}
	
	public TreeNode getRchild() {  
        return rchild;  
    }  
    public void setRchild(TreeNode rchild) {  
        this.rchild = rchild;  
    }  
    public TreeNode getLchild() {  
        return lchild;  
    }  
    public void setLchild(TreeNode lchild) {  
        this.lchild = lchild;  
    }
	
	public String getStr(){
		return str;
	}
	
	/**
	 * 获取每个节点的运算结果,并检验除法
	 * 1)除数为0
	 * 2)不能整除
	 * 出现以上两种情况的话将该运算符转换成其他三种运算符
	 * 
	 * @return result
	 */
	public String getResult(){
		if(hasChild()){
			switch(str){
				case "+":
					return String.valueOf(Integer.parseInt(getLchild().getResult()) + Integer.parseInt(getRchild().getResult()));
				case "-":
					return String.valueOf(Integer.parseInt(getLchild().getResult()) - Integer.parseInt(getRchild().getResult()));
				case "*":
					return String.valueOf(Integer.parseInt(getLchild().getResult()) * Integer.parseInt(getRchild().getResult()));
				case "/":
					if(getRchild().getResult().equals("0")){
						while(str.equals("/")){
							str = String.valueOf(Ran.getOperator());
						}
						return this.getResult();
					}
					else if(Integer.parseInt(getLchild().getResult()) % Integer.parseInt(getRchild().getResult()) != 0){
						while(str.equals("/")){
							str = String.valueOf(Ran.getOperator());
						}
						return this.getResult();
					}
					else
						return String.valueOf(Integer.parseInt(getLchild().getResult()) / Integer.parseInt(getRchild().getResult()));
			}
		}
		return str;
	}     
    
    /**
     * 先对每个运算式添加括号,然后根据去括号法则,去掉多余的子式的括号
     * 
     * @return string
     */
    public String toString(){
    	String Lstr = "", Rstr = "", Str = "";
    	if(hasChild()){
    		//右子树如果有孩子,说明右子树是一个表达式,而不是数字节点。
    		if(getRchild().hasChild()){                         
    			//判断左邻括号的运算符是否为'/'
    			if(str.equals("/")){
    				//获取右子树的表达式,保留括号
    				Rstr = getRchild().toString();              
    			}
    			//判断左邻括号的运算符是否为'*'或'-'
    			else if(str.equals("*") || str.equals("-")){
    				//判断op是否为'+'或'-'
    				if(getRchild().str.equals("+") || getRchild().str.equals("-")){	
    					Rstr = getRchild().toString();			
    				}
    				else{
    					//获取右子树的表达式,并且去括号 
    					Rstr = getRchild().toString().substring(1, getRchild().toString().length()-1);	
    				}
    			}
    			else{
    				//右子树除此之外都是可以去括号的。
    				Rstr = getRchild().toString().substring(1, getRchild().toString().length()-1);		
    			}
    		}
    		else{
    			Rstr = getRchild().str;
    		}
    		//左子树的情况同右子树类似
    		if(getLchild().hasChild()){												
    			if(str.equals("*") || str.equals("/")){
    				if(getLchild().str.equals("+") || getLchild().str.equals("-")){
    					Lstr = getLchild().toString();
    				}
    				else{
    					Lstr = getLchild().toString().substring(1, getLchild().toString().length()-1);
    				}
    			}
    			else{
    				Lstr = getLchild().toString().substring(1, getLchild().toString().length()-1);
    			}
    		}
    		else{
    			Lstr = getLchild().str;
    		}
    		//获取当前的运算式,并加上括号
    		Str = "(" + Lstr + str + Rstr + ")"; 									
    	}
    	else{
    		//若没有孩子,说明是数字节点,直接返回数字
    		Str = str;
    	}
    	return Str;
    }
    
    public boolean hasChild(){
    	if(lchild == null && rchild == null)
    		return false;
    	else
    		return true;
    }
}

图形界面生成及输出

public static void InitUI()
    {
    	JFrame frame =new JFrame();
    	frame.setTitle("四则运算");
    	frame.setSize(500, 500);
    	frame.setLocationRelativeTo(null);
    	frame.setDefaultCloseOperation(3);
    	
    	FlowLayout f1=new FlowLayout(FlowLayout.LEFT);
    	frame.setLayout(f1);
    	
    	JLabel name1=new JLabel("请输入题目个数:");
    	frame.add(name1);
    	
    	JTextField name1text=new JTextField();//文本框
    	name1text.setPreferredSize(new Dimension(50, 30));
    	frame.add(name1text);
    	
    	JLabel name=new JLabel();//空
    	name.setPreferredSize(new Dimension(110,30));
    	frame.add(name);
    	
    	JLabel name2=new JLabel("请输入最大值:");
    	frame.add(name2);
    	
    	JTextField name2text=new JTextField();//文本框
    	name2text.setPreferredSize(new Dimension(50, 30));
    	frame.add(name2text);
    	
    	JLabel name4=new JLabel("是否输出到文件:");
    	frame.add(name4);
    	
    	JRadioButton jrb1, jrb2;
    	jrb1 = new JRadioButton("是");
        jrb2 = new JRadioButton("否");
        frame.add(jrb1);
        frame.add(jrb2);
        
        JLabel name5=new JLabel();//空
    	name5.setPreferredSize(new Dimension(110,30));
    	frame.add(name5);
        
        JButton bu=new JButton("生成");
        bu.setPreferredSize(new Dimension(80,30));
        frame.add(bu);
        
    	JLabel name3=new JLabel();//空
    	name3.setPreferredSize(new Dimension(50,30));
    	frame.add(name3);
    	
    	JLabel name7=new JLabel("题目:");
    	frame.add(name7);
    	
    	JLabel name6=new JLabel();//空
    	name6.setPreferredSize(new Dimension(250,30));
    	frame.add(name6);
    	
        JTextField Titletext=new JTextField();//文本框
    	Titletext.setPreferredSize(new Dimension(450, 300));
    	frame.add(Titletext);
    	
    	
		/**
		 * 事件监听器
		 */
    	bu.addActionListener(new ActionListener(){
    		public void actionPerformed(ActionEvent e) {
    			String a=name1text.getText();
    			int Title_Num = Integer.parseInt(a);  //string转换为int
    			String b=name2text.getText();
    			maxNum = Integer.parseInt(b);  //string转换为int
    			String[] Title=new String[Title_Num];
				String[] result1=new String[Title_Num];
    			for(int i = 0; i < Title_Num; i++){
    				BinaryTree bTree;
    				bTree = new BinaryTree(2);
    				bTree.createBTree();
    				String result = bTree.CalAndVal();
    				Title[i]=bTree.toString();
    				result1[i]=result;
    				System.out.println(Title[i]+"="+result1[i]);
    			}
    			
    			
    				Titletext.setText(getStringByArray(Title,result1));
    			
    			/**
    			 * 文件输出
    			 */
    			if(jrb1.isSelected()) {
    				try{
						PrintStream mytxt=new PrintStream("./test.txt");
						PrintStream out=System.out;
						System.setOut(mytxt);
						for(int i = 0; i < Title_Num; i++){
							System.out.println(Title[i] + "=" + result1[i]);
						}
						System.setOut(out);
						}catch(FileNotFoundException e1){
						e1.printStackTrace();
						}
    			}
    			
    		}
    	});
    	
    	frame.setVisible(true);
    }
	/**
	 * 
	 * @将题目整和成一个字符串,方便setText输出
	 * @param b
	 * @return str
	 */
	public static String getStringByArray(String[] a,String[] b) {
	    String str = "";
	    for(int i=0;i<a.length;i++) {
	        str += a[i];
	        str += '=';
	        str += b[i];
	        str += ' ';
	        str += ' ';
	        str += ' ';
	        str += ' ';
	    }
	    return str;
	}

四.运行结果截图

图形框架截图

输出到文本框

输出到文件

五.参考资料

随机生成四则运算式及答案(含括号)
如何将java的输出保存到文件中作为程序日志
Java实现图形界面
Java 图形化界面设计(GUI)实战练习(代码)
Java如何画图形用户界面
java按钮的监听事件
java图形界面中怎么用settext输出一个数组

六.工作照片

七.评价及总结

 在这次的作业中,我的领航员是冯俊鹏同学,他在过程中给了我很大的帮助,不断地督促我尽快完成,遇到不会的问题和我一起解决,是使我的代码更加完善,没有他的话我很难完成这次的作业。
 通过这次作业,我完成了我的第一个拥有图形界面的作品,在这之前一直都是用黑框进行操作,编写代码的过程是非常困难的,有许许多多的问题根本不知道如何解决,例如图形化界面的实现、界面的输入和输出和事件监听器的编写等等,碰到这些问题只能去网上搜索 ,找了很多的参考文章,上面列举出的仅仅只是其中一小部分,程序实现了一部分功能,但还有几个功能没有实现,例如选择小数和选择括号,图形界面的编写也比较混乱,但是,在这次的代码编写过程中,我学到了很多以前根本不会的知识,收获了很多。

原文地址:https://www.cnblogs.com/-yanghao/p/10810083.html