首页 > 其他分享 >【个人项目互评】——中小学数学试卷生成系统

【个人项目互评】——中小学数学试卷生成系统

时间:2023-09-20 21:15:08浏览次数:38  
标签:题目 String System 互评 println 数学试卷 operand 中小学 out

项目名称:中小学数学试卷生成系统
编程语言:java
代码作者:符南山
评价人 :秦凯


一、简介

我与符南山同学结对,并且都是采用java语言。因此下面博客,我们以java的角度来测试分析南山同学的代码,功能。通过这次互评来互相学习彼此间的优点,同时认识到自己的不足,来提升自己的编程思维。

二、项目需求

1、命令行输入用户名和密码,两者之间用空格隔开(程序预设小学、初中和高中各三个账号,具体见附表),如果用户名和密码都正确,将根据账户类型显示“当前选择为XX出题”,XX为小学、初中和高中三个选项中的一个。否则提示“请输入正确的用户名、密码”,重新输入用户名、密码;

2、登录后,系统提示“准备生成XX数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):”,XX为小学、初中和高中三个选项中的一个,用户输入所需出的卷子的题目数量,系统默认将根据账号类型进行出题。每道题目的操作数在1-5个之间,操作数取值范围为1-100;

3、题目数量的有效输入范围是“10-30”(含10,30,或-1退出登录),程序根据输入的题目数量生成符合小学、初中和高中难度的题目的卷子(具体要求见附表)。同一个老师的卷子中的题目不能与以前的已生成的卷子中的题目重复(以指定文件夹下存在的文件为准,见5);

4、在登录状态下,如果用户需要切换类型选项,命令行输入“切换为XX”,XX为小学、初中和高中三个选项中的一个,输入项不符合要求时,程序控制台提示“请输入小学、初中和高中三个选项中的一个”;输入正确后,显示“”系统提示“准备生成XX数学题目,请输入生成题目数量”,用户输入所需出的卷子的题目数量,系统新设置的类型进行出题;

5、生成的题目将以“年-月-日-时-分-秒.txt”的形式保存,每个账号一个文件夹。每道题目有题号,每题之间空一行;

附表1:账户,密码

附表-2:小学,初中,高中题目难度要求

三、项目文件的结构

在src文件夹下

  1. project包存放五个类 是代码的实现

  2. question文件夹 用来存放所有老师的文件夹

  3. teachers.txt 文件用来存放老师的账户信息

四、代码分析与测试

1.代码分析

1.1 Main类:

里面存放主函数main(),整个程序运行的端口;

main()函数:

  public static void main(String[] args) {
    GenerativeSystem generativeSystem = new GenerativeSystem();
    generativeSystem.runSystem();
  }
1.2 Teacher类: 抽象类

定义老师的属性,get函数获取属性;

1.3 MathTeacher类: 数学老师类
1)继承 Teacher类;
2)private属性:

List: allQuestion (用来记录当前老师已经生成题目列表) 用来实现后面的查重功能;

3)函数:

有参构造函数: 创建老师文件夹;

 public MathTeacher(String tType, String tAccount, String tPassword) {
    type = tType;
    account = tAccount;
    password = tPassword;
    allQuestion = new ArrayList<>();
    /* 如果数学老师文件夹没有创建,则新建一个文件夹 */
    String dirname = "src\\questions\\" + account;
    File d = new File(dirname);
    if (d.mkdir()) {
      System.out.println(account + "的文件夹已创建");
    }
  }

readQuestion() : 来读取该老师文件夹下的题目 存放到列表 allQuestion中;

public void readQuestion() {
    try {
      String filePath = "src" + File.separator + "questions" + File.separator + account;
      File file = new File(filePath);
      String[] fileList = file.list();
      if (fileList != null) {
        for (int i = 0; i < fileList.length; i++) {
          FileReader fileReader = new FileReader(filePath + "\\" + fileList[i]);
          BufferedReader reader = new BufferedReader(fileReader);
          String str;
          while ((str = reader.readLine()) != null) {
            if (str.equals("")) {
              str = reader.readLine();
            }
            allQuestion.add(str.substring(str.indexOf(" ") + 1));
          }
        }
      }
    } catch (Exception exception) {
      System.out.println("读取老师题目文件失败");
    }
  }

getQuestion():来获得list变量allQuestion;

public List<String> getQuestion() {
    return allQuestion;
  }

addQuestion(String aQuestion):添加题目到allQuestion,主要用于后面生成一道新题目,满足要求,那么需要这样加入到allQuestion中,完善查重功能;

  public void addQuestion(String aQuestion) {
    allQuestion.add(aQuestion);
   }
1.4 GenerativeSystem类: 题目生成系统
1)private属性:

mathTeachers 存放老师列表;

map 记录用户是否读取了对应文件夹下的题目;

2)函数:

无参构造函数 :调用readTeachers() 从teachers.txt文件中读取所有老师列表存放到mathTeachers链表中;

  public GenerativeSystem() {
    mathTeachers = new ArrayList<>();
    readTeachers();
  }

runSystem() :调用login()函数判断是否登录成功,登录成功之后调用generateQuestion函数生成题目;

public void runSystem() {
    while (true) {
      System.out.println("----------登录----------");
      System.out.println("请输入用户名 密码(空格分隔,输入-1退出系统):");
      Scanner sc = new Scanner(System.in);
      String account = sc.next();
      if (account.equals("-1")) {
        System.out.println("退出系统");
        break;
      }
      String password = sc.next();
      MathTeacher user = login(account, password);

      if (user == null) {
        System.out.println("请输入正确的用户名、密码");
      } else {
        System.out.println("----------------------");
        System.out.println("登录成功");
        /* 没有读取对应文件夹下的已生成题目 */
        if (map.get(user.getAccount()) == 0) {
          user.readQuestion();
          map.put(user.getAccount(), 1);
        }
        QuestionGeneration questionGeneration = new QuestionGeneration();
        questionGeneration.generateQuestion(user);
      }
    }
  }

readTeacher(): 通过读取teacher.txt文件将老师的账户信息 存储到 mathTeacher中 ;

优点:通过这样的方式如果后期添加老师,那么只需要在txt文件中添加相应的信息,便于后期的维护;

在这里,我是直接讲老师的信息存放在程序中,这一点我需要改进优化;

public void readTeachers() {
    try {
      BufferedReader reader = new BufferedReader(new FileReader("src\\teachers.txt"));
      String str;
      while ((str = reader.readLine()) != null) {
        String[] buff = str.split(" ");
        MathTeacher temp = new MathTeacher(buff[0], buff[1], buff[2]);
        mathTeachers.add(temp);
        map.put(buff[1], 0); // 是否读取题目文件标记设置为0
      }
    } catch (Exception exception) {
      System.out.println("文件打开失败");
    }
  }

login(): 判断是否登录成功。通过循环遍历mathTeacher列表 判断是否满足登录条件;

  public MathTeacher login(String account, String password) {
    for (MathTeacher mathTeacher : mathTeachers) {
      if (account.equals(mathTeacher.getAccount())
          && password.equals(mathTeacher.getPassword())) {
        return mathTeacher;
      }
    }
    return null;
  }

saveFile(): 通过循环 将生成的题目保存到该老师文件夹下,并且生成.txt文件;

  public static void saveFile(MathTeacher mathTeacher, List<String> temp) {
    Date date = new Date();
    SimpleDateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
    String path = "src\\questions\\" + mathTeacher.getAccount() + "\\"
        + dataFormat.format(date) + ".txt";
    File file = new File(path);
    try {
      if (!file.exists()) {
        if (file.createNewFile()) {
          System.out.println("文件src\\questions\\" + mathTeacher.getAccount()
              + "\\" + dataFormat.format(date) + ".txt已创建");
        }
      }
      BufferedWriter bw = new BufferedWriter(new FileWriter(path));
      int sizes = temp.size();
      for (int i = 0; i < sizes; i++) {
        bw.write(temp.get(i));
        if (i != (sizes - 1)) {
          bw.write("\n\n");
        }
      }
      bw.close();
    } catch (Exception exception) {
      System.out.println("文件创建失败");
    }
  }
1.5 QuestionGeneration :生成题目的类
1)private属性: 无 ;
2)函数:

generateQuestion():接收当前登录的老师,以及他所属的类型; 进入while循环,printOption()出示选择界面,然后调用judgeNum(),判断是否满足要求,

用num变量接收该数字;switch通过num的值,执行不同的操作; -1 break 退出登录,0 切换难度,调用changeQuestionType(),1 则开始生成题目,输入题目数量,若满足 ,则调用mathmaticalQuestion()函数,开始生成题目;

public void generateQuestion(MathTeacher loginTeacher) {
    String order;
    int num;
    String questionType = loginTeacher.getType();
    Scanner sc = new Scanner(System.in);
    System.out.println("当前选择为" + questionType + "出题");
    while (true) {
      printOptions(loginTeacher.getAccount(), loginTeacher.getType(), questionType);
      if ((num = judgeNum()) == -2) {
        continue;
      }
      switch (num) {
        case -1:
          System.out.println("退出当前用户,请重新登录\n");
          return;
        case 0:
          System.out.println("请输入切换为XX(XX为小学、初中和高中三个选项中的一个)。");
          order = sc.next();
          if (order.length() < 5) {
            System.out.println("输入项不符合要求,请输入切换为XX。");
          } else {
            questionType = changeQuestionType(questionType, order);
          }
          break;
        case 1:
          System.out.println("准备生成" + questionType + "数学题目,请输入生成题目数量(10-30):");
          if ((num = judgeNum()) == -2) {
            continue;
          }
          if (num < 10 || num > 30) {
            System.out.println("题目数量有效范围为10-30,请重新输入\n");
          } else {
            /* 生成数学题目 */
            mathematicalQuestion(loginTeacher, questionType, num);
          }
          break;
        default:
          System.out.println("请输入正确选项\n");
      }
    }
  }

printOption(): 打印选择界面,文字提示选择的选项;

优点:因为在while循环中,提出来这样一个重复的过程可以有效的减少方法的行数,简洁方便;

 public void printOptions(String account, String teacherType, String questionType) {
    System.out.println("----------------------");
    System.out.println("中小学数学卷子自动生成程序");
    System.out.println("用户:" + account + " 账户类型:" + teacherType);
    System.out.println("----------------------");
    System.out.println("操作选项:");
    System.out.println("-1.退出当前用户,重新登录");
    System.out.println(" 0.切换出题难度");
    System.out.println(" 1.开始生成题目");
    System.out.println("当前出题难度为:" + questionType);
    System.out.print("请输入选项:");
  }

changeQuestionType() : 修改题目类型(小学,初中,高中);

  public String changeQuestionType(String oldType, String order) {
    String head = order.substring(0, 3);
    String changeType = order.substring(3);
    if (head.equals("切换为") && (changeType.equals("小学") || changeType.equals("初中")
        || changeType.equals("高中"))) {
      System.out.println("\n出题难度已切换为:" + changeType);
      return changeType;
    } else {
      System.out.println("输入项不符合要求,请输入小学、初中和高中三个选项中的一个");
      return oldType;
    }
  }

judgeNum():判断所输入的是否为数字;

优点:避免用户输入非数字的类型而导致程序崩溃;

在这里南山使用的判断输入是否为数字,我是通过try catch捕获异常,来实现,两者应该都可以;

  public int judgeNum() {
    int num;
    Scanner scanner = new Scanner(System.in);
    if (scanner.hasNextInt()) {
      num = scanner.nextInt();
      scanner.nextLine();
      if (num == -2) {
        return -3;
      }
      return num;
    } else {
      scanner.nextLine();
      System.out.println("请输入正确数字\n");
      return -2;
    }
  }

mathmaticalQuestion() :来生成题目的函数,调用getAQuestion来获取一道题目,然后进行查重判断,若满足那么temp.add,最后saveFile函数保存;

  public void mathematicalQuestion(MathTeacher mathTeacher, String type, int num) {
    List<String> temp = new ArrayList<>();
    for (int i = 1; i <= num; i++) {
      String result;
      result = getAQuestion(type);
      /* 有重复题目则重新生成 */
      if (duplicateChecking(mathTeacher, result)) {
        i--;
        continue;
      }
      mathTeacher.addQuestion(result);
      result = i + ". " + result;
      temp.add(result);
    }
    GenerativeSystem.saveFile(mathTeacher, temp);
    System.out.println(type + "数学题目已生成,数量为" + num);
  }

getAQuestion():通过类型判断出操作数的个数,利用随机数先生成操作数的大小,调用addBracket()函数添加括号,若为初中,高中,那么再调用sqrtAndSquare()函数添加平方或者根号,若为高中,那么再调用trigonometricFunction()添加三角函数; 最后将运算符添加到位;

优点:根据附表二的要求,将添加括号,平方根号,三角函数这些分别进行拆分,便于后期维护;
我的做法:是设置了一个接口,然后分别定义了三个类来继承接口,分别实现,相较于南山这种实现方法,显然我的那种代码长度更长,后期维护性也比较差,尽管使用了多态。。还有待改进

一个新思路(应该定义接口,三个类分别继承接口实现,同时将括号, 平方根号,三角函数拆分成三个函数,这样实现了多态,也实现了函数拆分,便于优化和后期维护);

 public String getAQuestion(String type) {
    char[] symbol = new char[]{
        '+', '-', '*', '/'
    };
    Random r = new Random();
    StringBuilder result = new StringBuilder();
    String[] operand;
    int operandNum;
    if (type.equals("小学")) {
      operandNum = r.nextInt(4) + 2;
    } else {
      operandNum = r.nextInt(5) + 1;
    }
    /* 生成操作数 */
    operand = new String[operandNum];
    for (int i = 0; i < operandNum; i++) {
      operand[i] = String.valueOf(r.nextInt(100) + 1);
    }
    /* 添加括号 */
    operand = addBracket(operand, operandNum);
    /* 添加平方或根号 */
    if (type.equals("初中") || type.equals("高中")) {
      operand = sqrtAndSquare(operand, operandNum);
    }
    /* 添加三角函数 */
    if (type.equals("高中")) {
      operand = trigonometricFunction(operand, operandNum);
    }
    /* 添加运算符 */
    for (int i = 0; i < operandNum; i++) {
      result.append(operand[i]);
      if (i != operandNum - 1) {
        result.append(symbol[r.nextInt(4)]);
      }
    }
    result.append("=");
    return result.toString();
  }

addBracket():添加括号

  public String[] addBracket(String[] operand, int operandNum) {
    Random r = new Random();
    int bracketNum = 0;
    for (int i = 0; i < operandNum; i++) {
      int temp = r.nextInt(3);
      if (temp == 1 && i < operandNum - 1) {
        operand[i] = "(" + operand[i];
        bracketNum++;
      } else if ((temp == 2) && bracketNum > 0) {
        operand[i] = operand[i] + ")";
        bracketNum--;
      }
      if (i == operandNum - 1 && bracketNum > 0) {
        while (bracketNum > 0) {
          operand[i] = operand[i] + ")";
          bracketNum--;
        }
      }
    }
    return operand;
  }

sqrtAndSquare():添加平方根号

 public String[] sqrtAndSquare(String[] operand, int operandNum) {
    Random r = new Random();
    boolean flag = false;
    for (int i = 0; i < operandNum; i++) {
      int temp = r.nextInt(6);
      if (temp == 1) {
        operand[i] = "√" + operand[i];
        flag = true;
      } else if (temp == 2) {
        operand[i] = operand[i] + "²";
        flag = true;
      }
    }
    /* 没有根号或平方 */
    if (!flag) {
      int index = r.nextInt(operandNum);
      if (r.nextBoolean()) {
        operand[index] = "√" + operand[index];
      } else {
        operand[index] = operand[index] + "²";
      }
    }
    return operand;
  }

trigonnometricFunction() : 添加三角函数

 public String[] trigonometricFunction(String[] operand, int operandNum) {
    Random r = new Random();
    String[] tri = {"sin", "cos", "tan"};
    boolean flag = false;
    for (int i = 0; i < operandNum; i++) {
      if (r.nextInt(3) == 1) {
        operand[i] = tri[r.nextInt(3)] + operand[i];
        flag = true;
      }
    }
    if (!flag) {
      int index = r.nextInt(operandNum);
      operand[index] = tri[r.nextInt(3)] + operand[index];
    }
    return operand;
  }

duplicateChecking():查重函数

public boolean duplicateChecking(MathTeacher mathTeacher, String checkString) {
    List<String> temp = mathTeacher.getQuestion();
    return temp.contains(checkString);
  }

2.测试

2.1 登录功能测试

优点:登录界面简洁,具有文字提示;


登录失败有文字提示,并且还可以继续登录;


用户名与密码输入正确,那么成功登录;

2.2 登录成功后,界面展示


优点:此时用分割线---进行分离,界面美观;
这里我是使用****来分割,个人感觉还是---会更美观,值得学习;


当输入不合规范时,程序不会崩溃,这里就体现了前面judgeNum()函数的重要性了;

2.3 切换难度


只有当输入切换为小学/初中/高中,才会切换难度,功能正确;

2.4 出题

当输入题目数量在10-30之间会生成题目;

当输入题目数量不满足,则不会生成;

小学题目:

初中题目:

高中题目:

小学要求(+-*/) 都考虑到了,并且操作数范围是2-5 ,初中(要求至少一个平方或根号),操作数范围是1-5,高中题目(要求至少一个三角函数),操作数范围是1-5; 出题满足以上规范,功能正确;

2.5 试卷保存格式


出题文件的保存命名与格式也正确;


五、总结

针对于符南山的代码的编写与运行过程:

优点:

  • 老师的所有账户信息存储在teachers.txt文件中,这样便于老师信息的更改,便于后期维护。

  • 在出题函数中,将添加括号,添加平方根号,添加三角函数的步骤,拆分成三个方法,很巧妙。

  • 在输入时,总会使用judgeNum()函数来判断是否为整数,从而避免因用户输入不规范导致程序的崩溃。

  • 在运行中,界面的文字提示都很到位,既不会显的很臃肿,也很好的传达了意思。

不足:

  • 对于Teacher这个抽象类的使用不是很恰当,没有很好的利用上抽象类Teacher

最后还有一点,这个仁者见仁:

在运行中,切换难度如果输入不规范,在我看来,应该接着循环,让他输入想要切换的难度,南山的代码是让他重新回到选择的界面,这个大家都有不同的理解;

六、个人感悟

在分析测试完,南山所写的项目之后,感觉自己的代码还有不少可以改进的地方;

譬如:在出题函数我也可以将添加括号,添加平方根号,这些函数,独立拆分出来,这样便于后期优化维护;

同时,在程序运行过程中,自己的文字提示与界面显示 应该多加一个分割符------- 这样用户看起来更加简洁方便;

在与南山经过一起讨论分析之后,让我也认识到了互相学习,互相讨论的重要性;

这篇博客中,如果有哪个地方写的不正确,欢迎大家指出,批评;
互相讨论,学习,大家共同进步!

标签:题目,String,System,互评,println,数学试卷,operand,中小学,out
From: https://www.cnblogs.com/jiandan7/p/17717600.html

相关文章

  • 【HNU个人项目互评】 基于java生成中小学数学卷子自动生成程序代码分析
    【评价者】:金颖希【项目作者】:刘一凡【使用语言】:Java前言【项目简介】本项目为中小学数学卷子自动生成程序,采用java编程语言实现用户登录、用户类型判断、生成题目、题目查重、切换用户类型以及题目保存等简单功能。【评价标准】本文以代码分析为主,主要从代码的可读性、性能......
  • 个人项目互评
    中小学数学卷子自动生成程序分析本次课程任务要求我们在规定时间内完成一个可以自动生成中小学数学题目卷子的程序,用户为老师。主要是实现账户名密码的验证,账户的切换,根据账户类型生成不同难度的试题并查重。在此简单分析一下搭档小天的项目代码。 程序流程我来分析......
  • 个人项目互评
    项目介绍用户:小学、初中和高中数学老师。功能:命令行输入用户名和密码,两者之间用空格隔开(程序预设小学、初中和高中各三个账号,具体见附表),如果用户名和密码都正确,将根据账户类型显示“当前选择为XX出题”,XX为小学、初中和高中三个选项中的一个。否则提示“请输入正确的用户名、密码......
  • Hnu个人项目互评
    一、简介本文是对于编程队友xjm同学的代码互评,本着互相学习,互相进步的想法,学习对方的代码,找出其中的优点,缺点也要提醒队友改正。二、项目结构先简单分析一下队友项目中的类以及功能。项目中包括7个类和一个接口User类中点击查看代码privateStringuserName;private......
  • 个人项目互评
    一、前言本篇博客为对结对编程搭档李宛芸同学的个人项目(中小学数学卷子自动生成程序)的评价。首先感谢李宛芸同学,她在编程过程中多次帮忙检查我的代码并指出不符合规范的地方,省去了我很多查阅规定的时间.其次,我来对李宛芸同学的个人项目稍作评论,争取做到取长补短,共同进......
  • HNU_个人项目_中小学数学卷子自动生成程序_简要分析何梁雨代码
    一、前言感谢老师安排的这一次互评,以及我的结对编程伙伴何梁雨。在互评中我学到了不一样的编程思路,更清晰的感受到了自己编程水平哪一部分存在缺陷,并向这个方向学习改正。二、测试与评价1.测试程序运行(1)界面整洁简单,流程清晰。动作转折的地方经常会有一长串横杠隔开,让......
  • 湖南大学结对编程个人项目互评
    1.简介本博客用于湖南大学结对编程项目中对王明扬同学个人项目的互评工作。个人项目的功能是实现一个中小学数学试卷自动生成程序,基于命令行进行操作。2.代码2.1项目结构项目由多个包组成,分别执行不同的功能:2.1.1controller:接收用户的输入,调用service层进行相应的逻辑......
  • 【个人项目互评】————中小学生题目生成程序
    在完成个人项目的建设后,我和搭档互相交换了代码进行分析。在阅读过程中,看到了她代码的优势,也同时看到了自己代码的不足。再次写下这一次的项目分析;一、题目要求   二、测试与分析功能完成情况: 在输入正确的账户名以及密码后,能够获得当前用户年级信息以及后续操作步骤......
  • 【个人项目互评】结对互评-中小学数学试卷自动生成程序
    目录1.简介2.项目要求3.代码分析4.运行测试5.优缺点分析  1.简介本篇博客是对结对编程队友对于项目《中小学数学卷子自动生成系统》的学习,分析与总结,选用的编程语言为Java.  2.项目要求1、命令行输入用户名和密码,两者之间用空格隔开(程序预设小学、初中和高中......
  • 个人项目分析——中小学生数学卷子自动生成程序
    目录一.简介二.项目要求三.代码分析整体架构核心代码四.功能测试五.优缺点总结 一.简介本篇博客为对廖心怡同学的个人编程项目“中小学数学卷子自动生成程序”的分析与总结,在阅读代码的过程中学习到了许多优点,也发现了一些代码书写、代码结构等方......