首页 > 其他分享 >结对项目

结对项目

时间:2024-09-28 21:35:02浏览次数:1  
标签:结对 题目 String num2 项目 HashString1 public append

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13230
这个作业的目标 <结对完成实现一个自动生成小学四则运算题目的命令行程序>
项目成员 李响 3121002802
项目成员 欧可贵3121002717

一、Github链接

https://github.com/Placidoe/homework.git

二、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 40
Estimate 估计这个任务需要多少时间 10 10
Development 开发 150 200
· Analysis · 需求分析 (包括学习新技术) 60 120
Design Spec 生成设计文档 20 20
Design Review 设计复审 10 10
Coding Standard 需求分析 (包括学习新技术) 30 20
Design 具体设计 60 60
Coding · 具体编码 180 150
· Code Review · 代码复审 20 40
· Test · 测试(自我测试,修改代码,提交修改) 30 50
Reporting 报告 30 30
· Test Repor · 测试报告 20 20
· Size Measurement · 计算工作量 10 10
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 10 10
· 合计 650 780

三、需求

使用 -n 参数控制生成题目的个数,例如

Myapp.exe -n 10

将生成10个题目。

使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如

Myapp.exe -r 10

将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。

生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。
生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。
每道题目中出现的运算符个数不超过3个。
程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,23 + 45 = 和45 + 23 = 是重复的题目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)和1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+3和3+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。
生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:

四则运算题目1
四则运算题目2
……

其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。

在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:

答案1
答案2

特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。

程序应能支持一万道题目的生成。
程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:

Myapp.exe -e .txt -a .txt

统计结果输出到文件Grade.txt,格式如下:

Correct: 5 (1, 3, 5, 7, 9)

Wrong: 5 (2, 4, 6, 8, 10)

其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。

四、模块接口的设计与实现过程

两个类(DataStore、IOUtils)

DataStore 类是一个单例类,使用了双重检查锁定的方式来保证线程安全,并且提供了多种方法来生成和计算数学表达式。

public void insertOp():
随机选择一个操作符(+、-、*、/)并将其存入 tokens 数组。

public void insertNum():
随机生成一个操作数(范围为 0 到 num2)并将其转换为字符串形式存入 tokens 数组。

public void InitData(String[] args):
根据传入的参数初始化 op1、num1、op2 和 num2。如果参数数量不符合预期,则根据默认值进行初始化。

public void InitTokens():
初始化 tokens 数组并生成随机的操作数和操作符。

public int evalRPN(String[] tokens):
接收一个字符串数组(反向波兰表示法),使用栈计算结果。对于每个操作符,弹出相应的操作数进行计算,并将结果压回栈中。使用 StringBuilder 构建表达式的字符串。

public boolean isNumber(String token):
判断传入的字符串是否为数字。

public void Run():
运行生成表达式的主方法,循环指定次数生成数学表达式、计算结果,并校验表达式的唯一性。

public int caculate(String op,int num1,int num2):
根据传入的操作符和两个操作数计算并返回结果,处理基本的加、减、乘、除运算。

static public DataStore getInstance():
单例模式实现,确保在多线程环境中只创建一个 DataStore 实例。

IOUtils类

public static void saveExercises(String exercises):
参数:接收一个字符串参数 exercises,表示要保存的题目内容。
文件写入:
使用 BufferedWriter 包裹 FileWriter,使得写入操作更高效。
new FileWriter("Exercises.txt", true):以追加模式打开文件,如果文件不存在则会创建它。
writer.write(exercises):将题目写入文件。
writer.newLine():在写入的内容后添加换行符。
异常处理:
使用 try-with-resources 语句确保 BufferedWriter 在使用后自动关闭。
捕获 IOException 异常,并通过 e.printStackTrace() 打印异常信息。

public static void saveAnswers(String answers):
参数:接收一个字符串参数 answers,表示要保存的答案内容。
文件写入:采用相同的方式打开 Answers.txt 文件并写入内容。
异常处理:同样使用 try-with-resources 和 IOException 捕获,确保代码的安全性。

五、代码说明

DataStore.java文件:
整个 DataStore 类的设计思路是:

表达式生成: 随机生成一个数学表达式,确保操作数和操作符的随机性。
逆波兰表达式计算: 实现了一个计算逆波兰表达式的算法,能有效地处理各种操作符。
唯一性校验: 使用集合来确保生成的数学表达式是唯一的,避免重复。
这段代码的实现思路在于结合数学运算与数据结构(栈)来高效地生成和计算表达式,并通过策略来确保生成表达式的有效性和唯一性。

源代码:

点击查看代码
package com.lx.single;

import com.lx.utils.FractionGenerator;
import com.lx.utils.IOUtils;
import lombok.Data;

import java.util.*;

/**
 * TODO
 *
 * @Description
 * @Author Lx
 * @Date 2024/9/26 上午10:46
 **/
@Data
public class DataStore {
    private static volatile DataStore dataStore;

    /*
      策略1:
      1.从栈顶弹出a1-操作数
      2.
      从栈顶弹出a2-操作符(如果是+或-)则继续下一步单暂不运算,而是判断后面一个是否为(*或/),依次循环往后看,直到遇到(+或-)。
      如果是(*或/)继续下一步就直接进行运算
      3.从栈顶弹出a3-操作数
      4.从栈顶弹出a4-操作符
      ...
      ...
      ...

      策略2:补偿保证e1>=e2
      当弹出来操作数a1和操作符a2,判断a3,是否a1<a3,如果 a1<a3,则要把a1和a2弹出,重新,入栈一个新的操作数和操作符。直到满足了解,否则继续弹出。


      策略3:式子不能重复
      1.可以通过将式子字符串进行hash运算,然后存到set中,可能会存在误判,但结果肯定可以保证都是唯一的
     **/

    static String op1;
    static int num1;//生成的题目个数
    static String op2;
    static int num2;//生成的值的范围[0~num2)
    static Random random;
    static Stack<Object> st;//用来存放操作数和操作符。自栈顶向下,就是自表达式左向右
    static StringBuilder stringBuilder;
    static HashSet set;
    static StringBuilder HashString;
    static int index;
    static String[] tokens;

    String[] ops={"+","-","*","/"};
    static {
        num1=3;
        num2=2;
        random = new Random();
        st=new Stack<>();
        set=new HashSet();
        stringBuilder=new StringBuilder();
        tokens=new String[1000];
        HashString=new StringBuilder();
    }
    public void insertOp(){//入操作符
        int val = random.nextInt(4);
//        stringBuilder.append(ops[val]);
        tokens[index++]=ops[val];
    }

    public void insertNum(){//入操作数
//        stringBuilder.append(random.nextInt(num2+1));
        tokens[index++]= String.valueOf(FractionGenerator.generateTrueFraction());
    }
    public void InitData(String[] args){

        if(args.length==4){
            op1=args[0];
            num1=Integer.parseInt(args[1]);
            op2=args[2];
            num2=Integer.parseInt(args[3]);
        }else if(args.length==2){
            op1=args[0];
            num1=Integer.parseInt(args[1]);
        }

    }
    public void InitTokens(){
        tokens=new String[1000];
        index=0;
        //1.生成运算符的个数
        int val=random.nextInt(num2+1);
        int count = val==0?1:val;//左闭右开[0~num2)

        //2.入栈操作数和操作符
        insertNum();
        for(int i=0;i<count;i++){
            insertNum();// 1 2 + 3 / 4 - 6
            insertOp();
        }
    }

    public Double evalRPN(String[] tokens) {
        Deque<Double> stack = new LinkedList<Double>();
        Deque<String> express = new LinkedList<String>();

        express.push(tokens[0]);
        int n = index;
        for (int i = 0; i < n; i++) {
            String token = tokens[i];
            if (isNumber(token)) {
                stack.push(Double.parseDouble(token));
            } else {
                Double num2 = stack.pop();
                Double num1 = stack.pop();
//
                StringBuilder HashString1 = new StringBuilder();
                switch (token) {
                    case "+":
                        String str1 = express.pop();
                        HashString1.append("(");
                        HashString1.append(str1);
                        HashString1.append("+");
                        HashString1.append(num2);
                        HashString1.append(")");
                        express.push(HashString1.toString());

                        stack.push(num1 + num2);
                        break;
                    case "-":
                        String str2 = express.pop();
                        HashString1.append("(");
                        HashString1.append(str2);
                        HashString1.append("-");
                        HashString1.append(num2);
                        HashString1.append(")");
                        express.push(HashString1.toString());

                        stack.push(num1 - num2);
                        break;
                    case "*":
                        String str3 = express.pop();
                        HashString1.append("(");
                        HashString1.append(str3);
                        HashString1.append("*");
                        HashString1.append(num2);
                        HashString1.append(")");
                        express.push(HashString1.toString());
                        stack.push(num1 * num2);
                        break;
                    case "/":
                        if(num2==0)num2++;
                        String str4 = express.pop();
                        HashString1.append("(");
                        HashString1.append(str4);
                        HashString1.append("/");
                        HashString1.append(num2);
                        HashString1.append(")");
                        express.push(HashString1.toString());

                        stack.push(num1 / num2);
                        break;
                    default:
                }
            }
        }
        HashString=new StringBuilder(express.pop());
        return stack.pop();
    }

    public boolean isNumber(String token) {
        return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token));
    }


    public void Run(){

        //运行指定的次数
        for(int j=0;j<num1;j++){
            //1.初始化tokens
            InitTokens();
            for(int i=0;i<index;i++){
                System.out.println(tokens[i]);
            }
            //2.运算(运算+补偿+拼接表达式)
            Double res=evalRPN(tokens);
            IOUtils.saveExercises(HashString.toString());
            HashString.append("=");
            HashString.append(res);
            IOUtils.saveAnswers(HashString.toString());
            //3.校验表达式是否唯一,不唯一则要重试
            if(set.contains(HashString)){
                j--;
                HashString=new StringBuilder();
                continue;
            }
            set.add(HashString);
            //4.完成表达式
            System.out.println(HashString.toString());
            HashString=new StringBuilder();
        }
    }

    public int caculate(String op,int num1,int num2){
        switch (op){
            case "+":
                return num1+num2;
            case "-":
                if(num1<num2){

                    return num2-num1;
                }
                return num1-num2;
            case "*":
                return num1*num2;
            case "/":
                return num1/num2;
            default:
                return 0;
        }
    }

    static public DataStore getInstance(){
        if(dataStore==null){
            synchronized (DataStore.class){
                if(dataStore==null)
                    dataStore=new DataStore();
            }
        }
        return dataStore;
    }
}

IOUtils.java文件:
IOUtils 类提供了两个静态方法,saveExercises 和 saveAnswers,用于将练习题和答案分别写入不同的文本文件。这种设计允许用户在不创建 IOUtils 类实例的情况下,直接调用这些方法进行文件操作。异常处理确保了在写入文件过程中,如果发生任何输入输出错误,可以得到相关的错误信息以供调试。

源代码:

点击查看代码
package com.lx.utils;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

/**
 * IOUtils类用于处理输入和输出操作
 * @Author Okg
 * @Date 2024/9/28 下午8:01
 **/
public class IOUtils {

    // 存储题目到文件
    public static void saveExercises(String exercises) {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("Exercises.txt", true))) {
            // 将题目写入文件
            writer.write(exercises);
            writer.newLine(); // 换行
        } catch (IOException e) {
            e.printStackTrace(); // 打印异常信息
        }
    }

    // 存储答案到文件
    public static void saveAnswers(String answers) {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("Answers.txt", true))) {
            // 将答案写入文件
            writer.write(answers);
            writer.newLine(); // 换行
        } catch (IOException e) {
            e.printStackTrace(); // 打印异常信息
        }
    }
}

六、运行测试

测试结果:
Exercises.txt文件:

Answers.txt文件:

七、项目总结

  • 欧可贵:这是我第一次接触合作开发一个项目,所以有很多合作的部分会比较生疏,本地代码上传不了GitHub、GitHub上的代码拉不下来、java环境配置等等都是需要解决的问题,由于以前使用python较多,所以主要负责帮忙解决I/O功能,以及计算功能中的注释添加,计算功能由李响同学进行开发完成。当然这次结对项目让我认识到了团队协作的重要性,让我们更能清楚团队配合之间主要问题会存在哪里并解决它。
  • 李响:本次我主要负责主要功能的代码开发,看到题目我们经过讨论决定先随机生成一个数学表达式,然后使用逆波兰表达式处理各种操作符号,由于代码经验较丰富,所以我负责主要的代码开发。但是由于开发时间过长,破坏了我们原有的打算,效能分析没有能够完成(如果要完成的话可能会导致不能在规定时间内提交作业),我会吸取经验,争取下次做更加合理的时间规划,完成每一个任务要求。

标签:结对,题目,String,num2,项目,HashString1,public,append
From: https://www.cnblogs.com/3121002717ou/p/18438457

相关文章

  • 结对项目
    结对编程:小学四则运算这个作业属于哪个课程软件工程课程这个作业要求在哪里个人项目-作业-计科22级34班-班级博客-博客园(cnblogs.com)这个作业的目标成员一迪力拜尔3222004889成员二坤杜孜阿依3222004768github链接 https://github.com/......
  • 黑马PM-内容项目-产品生产发布
    产品生产发布流程项目管理产品需求评审......
  • 结对项目
    这个作业属于哪个课程22级计科2班这个作业要求在哪里作业要求这个作业目标实现一个自动生成小学四则运算题目的命令行程序(也可以用图像界面,具有相似功能)。Github地址:https://github.com/JiangJiazhe/PairWork/tree/main项目设计函数功能main模块:设置mai......
  • 结对项目——四则运算
    结对项目——四则运算这个作业属于哪个课程软工22级计科12班这个作业的要求在哪里作业要求这个作业的目标实现四则运算的结对编程项目成员姓名学号GitHub链接分工谭立业3122004365github项目功能的基本实现,博客的编写罗锴佳3122001905gith......
  • 结对项目
    成员姓名学号李心怡3222004336郑梦瀚3222004340作业这个作业属于哪个课程班级链接这个作业要求在哪里作业链接这个作业的目标二人合作实现一个自动生成小学四则运算题目的命令行程序Github链接:点击此处PSP表格PSP2.1PersonalSoftwar......
  • 结对项目
    这个作业属于哪个课程计科22级12班这个作业要求在哪里作业要求这个作业的目标实现一个自动生成小学四则运算题目的命令行程序姓名&学号:姓名学号董雯霖3122004780陈金星3122004774GitHub链接:https://github.com/1534063091/Arithmometer一......
  • 结对项目
    这个作业属于哪个课程计科2班这个作业要求在哪里作业要求这个作业的目标<要求实现一个自动生成小学四则运算题目的命令行程序>[作业地址](https://github.com/cr1017/3122004813)一、时间预估及实际花费时间PSP表格PSP2.1PersonalSoftwareProcessS......
  • 结对项目
    这个作业属于哪个课程课程这个作业的要求在哪里结对项目这个作业的目标实现一个自动生成小学四则运算题目的命令行程序一、合作成员项目成员学号github仓库地址黄锐3222004335github王伊若3222004382PSP2.1PersonalSoftwareProcess......
  • 结对项目
    这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13230这个作业的目标组队编程实现四则运算的项目组队成员阿依古再丽(3222004721)、张汉洁(3222004598)这......
  • 结对项目
    姓名学号Github项目地址薛秋昊3122004369https://github.com/0818XR/0818XR/tree/main/3122004369/GenerateArithmeticProblems曾俊涛3122004373一.PSP表格PSP2.1PersonalSoftwareProcessStages预估耗时(分钟实际耗时(分钟)Planning计划1010......