OOP第一次Blog作业

前言

这学期,我们在C语言的基础上学习了一门新语言——java。从C语言到java的转变也是从面向过程到面向对象的转变,这其中免不了有一些语法和设计思路的不同,而作为初学者的我们极容易将C语言的设计思维代入java的学习,这无疑给我们学习增加了难度,更胜者导致无法领悟“面向对象”要点,这时候实践的重要性就显而易见了。
以下是三次PTA练习的总结性报告。

  • 知识点
  1. 类的设计
  2. 类与类之间的关系
  3. 正则表达式的理解与灵活运用
  4. 字符串的处理(包括拆分和删除)
  5. 复杂问题的思考(将问题分块化,化繁为简)
  6. 基础的调试
  • 题量
    题量尚可,基本上可以完成,前三次为5+4+3的模式,前面几题为基础题,每次作业的重点为最后一题,这最后一题具有扩展性,即可一直迭代,增加需求。以下的重点也在此。
  • 难度
    个人认为其实题目难度适中,难的是思考设计哪些类及类与类之间的关系和题目内容本身的逻辑关系,最重要的是调试过程中的修改,比如有一些测试点是要去除空格及考虑空字符,如果没有考虑这些也就过不了某些测试点了。

设计与分析

nchu-software-oop-2024-1-5

7-5 答题判题程序-1
分数 74 困难 作者 蔡轲 单位 南昌航空大学
设计实现答题程序,模拟一个小型的测试,要求输入题目信息和答题信息,根据输入题目信息中的标准答案判断答题的结果。

输入格式:
程序输入信息分三部分:

1、题目数量

格式:整数数值,若超过1位最高位不能为0,

样例:34

2、题目内容

一行为一道题,可以输入多行数据。

格式:"#N:"+题号+" "+"#Q:"+题目内容+" "#A:"+标准答案

格式约束:题目的输入顺序与题号不相关,不一定按题号顺序从小到大输入。

样例:

 ``#N:1 #Q:1+1= #A:2
   #N:2 #Q:2+2= #A:4``

3、答题信息

答题信息按行输入,每一行为一组答案,每组答案包含第2部分所有题目的解题答案,答案的顺序号与题目题号相对应。

格式:"#A:"+答案内容

格式约束:答案数量与第2部分题目的数量相同,答案之间以英文空格分隔。

样例:#A:2 #A:78

  2是题号为1的题目的答案
  78是题号为2的题目的答案

答题信息以一行"end"标记结束,"end"之后的信息忽略。

输出格式:
1、题目数量

格式:整数数值,若超过1位最高位不能为0,

样例:34

2、答题信息

一行为一道题的答题信息,根据题目的数量输出多行数据。

格式:题目内容+" ~"+答案

样例:
1+1=~2
2+2=~4

3、判题信息

判题信息为一行数据,一条答题记录每个答案的判断结果,答案的先后顺序与题目题号相对应。

格式:判题结果+" "+判题结果

格式约束:

 1、判题结果输出只能是true或者false,
 2、判题信息的顺序与输入答题信息中的顺序相同

样例:true false true

  • 这一题可以说是最原始的题,基础题,但也有一些逻辑的思考,话不多说,分析如下:
    根据题目信息的不同,我设计了题目类,试卷类,答卷类及最后的主类:
    1.题目类:
    image
    2.试卷类:
    image
    3.答卷类:
    image
    4.主函数类

  • 基于以上分析类图如下:
    image

通过类图可以看出这次题目使用的类不是很多,主要用到了类的关联

nchu-software-oop-2024-2-4

7-4 答题判题程序-2
分数 73 困难

全屏浏览 切换布局 作者 蔡轲 单位 南昌航空大学
设计实现答题程序,模拟一个小型的测试,以下粗体字显示的是在答题判题程序-1基础上增补或者修改的内容。

要求输入题目信息、试卷信息和答题信息,根据输入题目信息中的标准答案判断答题的结果。

输入格式:

程序输入信息分三种,三种信息可能会打乱顺序混合输入:

1、题目信息

一行为一道题,可输入多行数据(多道题)。

格式:"#N:"+题目编号+" "+"#Q:"+题目内容+" "#A:"+标准答案

格式约束:

1、题目的输入顺序与题号不相关,不一定按题号顺序从小到大输入。
2、允许题目编号有缺失,例如:所有输入的题号为1、2、5,缺少其中的3号题。此种情况视为正常。

样例:#N:1 #Q:1+1= #A:2
         #N:2 #Q:2+2= #A:4

2、试卷信息

一行为一张试卷,可输入多行数据(多张卷)。

格式:"#T:"+试卷号+" "+题目编号+"-"+题目分值

 题目编号应与题目信息中的编号对应。

 一行信息中可有多项题目编号与分值。

样例:#T:1 3-5 4-8 5-2

3、答卷信息

答卷信息按行输入,每一行为一张答卷的答案,每组答案包含某个试卷信息中的题目的解题答案,答案的顺序与试卷信息中的题目顺序相对应。

格式:"#S:"+试卷号+" "+"#A:"+答案内容

格式约束:答案数量可以不等于试卷信息中题目的数量,没有答案的题目计0分,多余的答案直接忽略,答案之间以英文空格分隔。

样例:#S:1 #A:5 #A:22

   1是试卷号 

   5是1号试卷的顺序第1题的题目答案

   22是1号试卷的顺序第2题的题目答案

答题信息以一行"end"标记结束,"end"之后的信息忽略。

输出格式:

1、试卷总分警示

该部分仅当一张试卷的总分分值不等于100分时作提示之用,试卷依然属于正常试卷,可用于后面的答题。如果总分等于100分,该部分忽略,不输出。

格式:"alert: full score of test paper"+试卷号+" is not 100 points"

样例:alert: full score of test paper2 is not 100 points

2、答卷信息

一行为一道题的答题信息,根据试卷的题目的数量输出多行数据。

格式:题目内容+"~"+答案++"~"+判题结果(true/false)

约束:如果输入的答案信息少于试卷的题目数量,答案的题目要输"answer is null"   

样例:3+2=~5~true

  4+6=~22~false.

answer is null
3、判分信息

判分信息为一行数据,是一条答题记录所对应试卷的每道小题的计分以及总分,计分输出的先后顺序与题目题号相对应。

格式:题目得分+" "+....+题目得分+"~"+总分

格式约束:

1、没有输入答案的题目计0分

2、判题信息的顺序与输入答题信息中的顺序相同
   样例:5 8 0~13

根据输入的答卷的数量以上2、3项答卷信息与判分信息将重复输出。

4、提示错误的试卷号

如果答案信息中试卷的编号找不到,则输出”the test paper number does not exist”,参见样例9。

设计建议:

参考答题判题程序-1,建议增加答题类,类的内容以及类之间的关联自行设计。

  • 显而易见从这题就开始上难度了,我当时的思路:
    image

image
image
再加一个主类,类的设计并没有太大改变,只是类中的属性和方法有了增加,这样的弊端是不符合单一职责原则,某个类或是某个方法会承担许多的功能,这也是后续需要改进的地方。

  • 类图如下:
    image
    按这样的方法逻辑关系之间的传输数据会复杂许多,后面在意识到问题的时候已有做出改变。

nchu-software-oop-2024-3-3

7-3 答题判题程序-3
分数 84 困难
全屏浏览 切换布局 作者 蔡轲 单位 南昌航空大学
设计实现答题程序,模拟一个小型的测试,以下粗体字显示的是在答题判题程序-2基础上增补或者修改的内容,要求输入题目信息、试卷信息、答题信息、学生信息、删除题目信息,根据输入题目信息中的标准答案判断答题的结果。

输入格式:

程序输入信息分五种,信息可能会打乱顺序混合输入。

1、题目信息
题目信息为独行输入,一行为一道题,多道题可分多行输入。

格式:"#N:"+题目编号+" "+"#Q:"+题目内容+" "#A:"+标准答案

格式约束:
1、题目的输入顺序与题号不相关,不一定按题号顺序从小到大输入。
    2、允许题目编号有缺失,例如:所有输入的题号为1、2、5,缺少其中的3号题。此种情况视为正常。
样例:#N:1 #Q:1+1= #A:2
    #N:2 #Q:2+2= #A:4
     
2、试卷信息

试卷信息为独行输入,一行为一张试卷,多张卷可分多行输入数据。
格式:"#T:"+试卷号+" "+题目编号+"-"+题目分值+" "+题目编号+"-"+题目分值+...

格式约束:
题目编号应与题目信息中的编号对应。
一行信息中可有多项题目编号与分值。 
样例:#T:1 3-5 4-8 5-2
     
3、学生信息

学生信息只输入一行,一行中包括所有学生的信息,每个学生的信息包括学号和姓名,格式如下。

格式:"#X:"+学号+" "+姓名+"-"+学号+" "+姓名....+"-"+学号+" "+姓名     

格式约束:
答案数量可以不等于试卷信息中题目的数量,没有答案的题目计0分,多余的答案直接忽略,答案之间以英文空格分隔。
样例:
#S:1 #A:5 #A:22
    1是试卷号 
5是1号试卷的顺序第1题的题目答案 
4、答卷信息

答卷信息按行输入,每一行为一张答卷的答案,每组答案包含某个试卷信息中的题目的解题答案,答案的顺序号与试 卷信息中的题目顺序相对应。答卷中:

格式:"#S:"+试卷号+" "+学号+" "+"#A:"+试卷题目的顺序号+"-"+答案内容+...

格式约束:
答案数量可以不等于试卷信息中题目的数量,没有答案的题目计0分,多余的答案直接忽略,答案之间以英文空格分隔。
答案内容可以为空,即””。
答案内容中如果首尾有多余的空格,应去除后再进行判断。
样例:

       #T:1 1-5 3-2 2-5 6-9 4-10 7-3
       #S:1 20201103 #A:2-5 #A:6-4
       1是试卷号
       20201103是学号
       2-5中的2是试卷中顺序号,5是试卷第2题的答案,即T中3-2的答案 
       6-4中的6是试卷中顺序号,4是试卷第6题的答案,即T中7-3的答案 

注意:不要混淆顺序号与题号

5、删除题目信息

删除题目信息为独行输入,每一行为一条删除信息,多条删除信息可分多行输入。该信息用于删除一道题目信息,题目被删除之后,引用该题目的试卷依然有效,但被删除的题目将以0分计,同时在输出答案时,题目内容与答案改为一条失效提示,例如:”the question 2 invalid~0”

格式:"#D:N-"+题目号

格式约束:

   题目号与第一项”题目信息”中的题号相对应,不是试卷中的题目顺序号。

   本题暂不考虑删除的题号不存在的情况。      

样例:

#N:1 #Q:1+1= #A:2
#N:2 #Q:2+2= #A:4
#T:1 1-5 2-8
#X:20201103 Tom-20201104 Jack
#S:1 20201103 #A:1-5 #A:2-4
#D:N-2
end

输出
alert: full score of test paper1 is not 100 points
1+1=~5~false
the question 2 invalid~0
20201103 Tom: 0 0~0

答题信息以一行"end"标记结束,"end"之后的信息忽略。

输出格式:

1、试卷总分警示

该部分仅当一张试卷的总分分值不等于100分时作提示之用,试卷依然属于正常试卷,可用于后面的答题。如果总分等于100 分,该部分忽略,不输出。

格式:"alert: full score of test paper"+试卷号+" is not 100 points"

样例:alert: full score of test paper2 is not 100 points

2、答卷信息

一行为一道题的答题信息,根据试卷的题目的数量输出多行数据。

格式:题目内容+"~"+答案++"~"+判题结果(true/false)

约束:如果输入的答案信息少于试卷的题目数量,每一个缺失答案的题目都要输出"answer is null" 
样例:

     3+2=~5~true
     4+6=~22~false.
     answer is null

3、判分信息

判分信息为一行数据,是一条答题记录所对应试卷的每道小题的计分以及总分,计分输出的先后顺序与题目题号相对应。

格式:学号+" "+姓名+": "+题目得分+" "+....+题目得分+"~"+总分

格式约束:

 1、没有输入答案的题目、被删除的题目、答案错误的题目计0分
 2、判题信息的顺序与输入答题信息中的顺序相同

样例:20201103 Tom: 0 0~0

   根据输入的答卷的数量以上2、3项答卷信息与判分信息将重复输出。

4、被删除的题目提示信息

当某题目被试卷引用,同时被删除时,答案中输出提示信息。样例见第5种输入信息“删除题目信息”。

5、题目引用错误提示信息

试卷错误地引用了一道不存在题号的试题,在输出学生答案时,提示”non-existent question~”加答案。例如:

输入:

#N:1 #Q:1+1= #A:2
#T:1 3-8
#X:20201103 Tom-20201104 Jack-20201105 Www
#S:1 20201103 #A:1-4
end

输出:

alert: full score of test paper1 is not 100 points
non-existent question~0
20201103 Tom: 0~0

如果答案输出时,一道题目同时出现答案不存在、引用错误题号、题目被删除,只提示一种信息,答案不存在的优先级最高,例如:
输入:

#N:1 #Q:1+1= #A:2
#T:1 3-8
#X:20201103 Tom-20201104 Jack-20201105 Www
#S:1 20201103
end

输出:

alert: full score of test paper1 is not 100 points
answer is null
20201103 Tom: 0~0

6、格式错误提示信息

输入信息只要不符合格式要求,均输出”wrong format:”+信息内容。

  例如:wrong format:2 #Q:2+2= #4

7、试卷号引用错误提示输出

如果答卷信息中试卷的编号找不到,则输出”the test paper number does not exist”,答卷中的答案不用输出,参见样例8。

8、学号引用错误提示信息

如果答卷中的学号信息不在学生列表中,答案照常输出,判分时提示错误。参见样例9。

本题暂不考虑出现多张答卷的信息的情况。

  • 总结前面两题的不足,在做这道题的时候我多设计了许多类,如下:
    question类:
    image
    questions类:
    image
    paper类:
    image
    students类:
    image
    answer类:
    image
    delite类:
    image
    agent类:
    image
    check类:
    主要用来检查输入的字符串是否合格
    main类:
    主要用来测试

  • 类图如下:
    image

踩坑心得

  • 在进行键盘输入获取题目信息时,总达不到预期的结果,试了好几次都报错说引用为空字符,这让我百思不得其解,非常苦恼。后来想到了一个听起来很蠢但是很实用的方法:先分次输入,紧接着再输出,看看它到底是个什么“东西”。哈哈哈也许你也觉得这个方法蠢,但它真的超级好用哦,这不,问题解决了。最后发现输入的时候在题目数量和题目内容中还有一个类似于空字符的东西,它被读取后当然显示引用为空字符啦。
    所以我在读取输入的时候考虑了这个问题,如第一题:
String ko=reader.nextLine();
String[] input=new String[amount+5];
for(int i=1;i<=amount+40;i++){
    input[i]=reader.nextLine();
    if(input[i].equals("end"))
        break;
}//输入
  • 题目的输入顺序和题号并不一定是对应关系,在传入题目的时候要注意一下。就像我第一题是将题目按题号先排好序在传入,而后两题运用了哈希表,使用了哈希表键和值的对应关系,能将题目按题号的顺序储存,这样就省了排序这一步骤,方便不少。

  • 使用链表时,元素和数组一样是从0开始算的,而哈希表则是通过键来获取与其相对应的值。

  • 还有挺重要的一点就是类中各个属性数据类型的选择,我觉得这个东西选对了真的可以省好多事了,就比如数组,列表,哈希表这三者的选择,他们各有各的优点吧,我们要根据我们可能会用到的方法来进行选择:
    image
    如在questions类中,刚开始我用的列表来保存题目,后来发现列表不能做到题号与题目顺序的对应使后面的调用数据更加麻烦,在了解到哈希表是根据键和值的对应来存储数据时我果断的改了。

  • 题目说:输入信息只要不符合格式要求,均输出”wrong format:”+信息内容。但这个“不符合格式要求”非常值得推敲,这也是一个非常坑人的地方,比如#N:#A:等之前不能有东西,而#S:1 20201103 #A:1-5 #A:2-4中#A的后面则可以是空格、空字符甚至是没有东西都行,这就对正则表达式有着较高的要求

check1="#N:\\s*(\\d+)\\s*#Q:\\s*(.*?)\\s*#A:\\s*(.*?)\\s*";
check2="^#T:( )*\\d+( )*((\\d+(( )*)-(( )*)\\d+( )*)+)$";
check3="^#X:\\s*\\w+\\s(\\w+)+(?:-\\d+\\s\\w+)*$";
check4="^#S:( )*\\d+( )*\\w+\\s*(.*?)$";
check5="^#D:\\s*N\\s*-\\s*\\d+( )*$";
我感觉题目做出来这几个正则表达式功不可没!!!
  • 哈希表的遍历也是一个容易踩坑的地方,同时,这也是一个重难点以下是哈希表的几种遍历:
  1. 使用for-each循环和Map.Entry遍历键值对:
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("one", 1);
        map.put("two", 2);
        map.put("three", 3);

        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
        }
    }
}
  1. 使用for-each循环遍历键或值:
// 遍历键
for (String key : map.keySet()) {
    System.out.println("Key: " + key);
}

// 遍历值
for (Integer value : map.values()) {
    System.out.println("Value: " + value);
}

  1. 使用Iterator遍历:
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("one", 1);
        map.put("two", 2);
        map.put("three", 3);

        Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();
            System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
        }
    }
}

4.使用lambda表达式和forEach方法(Java 8及以上):

map.forEach((key, value) -> System.out.println("Key: " + key + ", Value: " + value));
  • 由于前面所讲#A:后面可能没有东西,但这种情况下还是要往答案里面传东西的,这个时候就要判断拆分后字符串的长度来判断要不要分情况讨论了,如第三题对#N:的处理:
	String[] tokens = s[i].split("\\#[A-Z]\\:");// 将题号,问题,答案分开
	if(tokens.length<4)
	{
		tokens[1] = tokens[1].replaceAll("^\\s+", "");// 删除空格
		tokens[1] = tokens[1].replaceAll("\\s+$", "");
		tokens[2] = tokens[2].replaceAll("^\\s+", "");
		tokens[2] = tokens[2].substring(0, tokens[2].length() - 1);
		int m0 = Integer.parseInt(tokens[1]);
		questions.keep(m0, tokens[2], "");
	}else{
for(int j=1;j<=3;j++){
    if(j==2){
        tokens[2] = tokens[2].replaceAll("^\\s+", "");
        tokens[2] = tokens[2].substring(0, tokens[2].length() - 1);
    }else{
    tokens[j]= tokens[j].replaceAll("^\\s+", "");//删除空格
    tokens[j]= tokens[j].replaceAll("\\s+$", "");
    }
}//""
    int m0=Integer.parseInt(tokens[1]);//将字符串转化为整数
    questions.keep(m0,tokens[2] , tokens[3]);
}

  • !!!我在判断格式的时候刚开始是根据字符串开头的字母来判断该怎么拆分和传数据,以至于我只判断了字符串的开头而没有考虑如果都没有符合的怎么办,这导致我那几个格式问题的测试点一直过不了,当时可烦躁了,想着我都写的这么“完美了”这么还过不了。最后在末尾加上
else{
                System.out.println("wrong format:"+s[i]);
            }

就行了

  • 还需要注意的是一切准备都做完了之后输出数据的顺序(我改了好多遍,终于写对了),比如最后一题:
                       for (int i = 1; i < titlenum; i++) {
			int j = paper.order.get(i);
			if (answer.answers.get(i) == null) {
				System.out.println("answer is null");
			} else {
				if (paper.title.get(i) == null) {
					System.out.println("non-existent question~0");
				} else {
					if ((delite.list.size() != 0) && (delite.isbedelite(j))) {
						System.out.println("the question " + i + " invalid~0");
						scores.put(i, 0);
					} else {
						System.out.println(
								paper.title.get(i).getQuestion() + "~" + answer.answers.get(i) + "~" + result.get(i));
					}
				}
			}
		}
  • 第三题题目做到最后的时候我还有一个测试点怎么也过不了,后来同学帮我写了几个测试点,发现如下测试点过不了:
#N:1 #Q:1+asdfasd    f1= #A:asfa asdfasdf
#N:2 #Q: 2+2= #A:4
#T:1 1-1 1-99
#X:20201103 Tom-20201104 Jack-20201104 Www
#S:1 20201103 #A:1-asfa asdfasdf2 #A:2-4
#D:N-2
#D:N-1
#D:N-3
#N:1 #Q:1+1= #A:asfa asdfasdf2
#N:2 #Q:2+2= #A:4
end

经过辛苦的调试发现之前写的程序,试卷中同一题目只能引用一次,也就是说后来相同的题号会被覆盖,经过修改终于得到了合格的代码。(由于其此次在许多的类中都有修改,比较分散,就不放代码了哈,放代码会感觉有点乱)

改进建议

  1. 题目一中首先是根据题号进行了分情况讨论,主要是因为当时第一次写这类题目,还很生疏,就想着用题号只有一题来测试一下,果然,比较好上手,改进建议就是不能将题号为一提取出来,要和题号大于一起讨论,这样减少了代码的复杂性。
  2. 后面两题呢,我觉得改进建议都一样,就放在一起讲了哈,主要是我感觉类的设计还不是那么的好,没有完全遵守单一职责原则,比如主函数,它承担的单子可重了,既要传输数据,还要判断字符串是否符合格式还要解析字符串,它承担的功能太杂了应该单独设计类来判断字符串是否符合规格和解析字符串,这样也方便了修改和加功能,这样也能使我们的思路更加清晰。

总结

在本阶段三次题目集的训练中,我只有两次拿了满分,而这唯一一次没拿满分是在第二次,是不是听着挺匪夷所思的,毕竟不是说这三次题目是递进的关系嘛,但确实是这样,哎,第二次没拿满分使我深深受到了打击,但在我不懈的专研下,第三题做出来了,这说明实力还是有点,之前就是犯懒!!!这值得深刻反思。经过这次的训练让我感触最深就是任何事情都要尝试一下,不尝试怎么知道自己不会做你,不能一开始就否定自己,潜力是要发掘的!!!

posted @ 2024-04-21 21:43  Linmo10  阅读(52)  评论(0)    收藏  举报