OOpre总结

OOpre课程结课总结 ——22373362杜启嵘 ​ 经历九周的OOpre课程的学习,我对面向对象编程有了基本的认识。从类、对象、方法等的基本概念,到最后一次迭代作业涉及到的设计模式,我循序渐进地学习到基本的面向对象编程思想(虽然我的代码仍然不够面向对象QAQ)。 一.代码的最终架构和过程中的重构 ​ 在五次作业的迭代中,我进行了多次代码的重构,前几次作业中的重构我认为是合理的,最后一次作业中的重构是把超过500行的类中的部分方法强行抽离出来放进静态方法类,并把参数分成几行避免行字数超过100字(面向OO-checkstyle的重构)。 1.第一次重构 ​ 在第三次作业中进行了实际上是两次重构,第一次在面临主类中的主方法行数超过60行,对方法中分指令执行的代码抽离出来声明为单独的方法,在编写JUnit时进行了第二次重构,由于第一版代码中解析输入逻辑与代码执行逻辑杂糅,而在编写JUnit时无法对输入进行操作,改为使用课程组提供的利用“二维数组”在统一存储输入内容,在inputhandler类中读取二维数组进行指令解析,这样的架构就能编写满足覆盖率的JUnit,下图为简单的架构图。 二.第二次重构 ​ 第四次作业中新增了战斗日志的概念,我一开始的处理中并没有对fightlog建类,只是在Adventure中建立容器来存储代表战斗日志的字符串,导致处理逻辑比较复杂,结果因为一个方法中的错误逻辑挂了强测,在修改强测的过程中完成了对代码的重构,新增fightlog类,架构图如下 三.第三次重构 ​ 在最后一次迭代开发中,由于类的行数限制,我不得不将一个类拆成两个类,在静态方法类中进行传参,属于是一次很丑陋的重构。 2.使用JUnit的心得体会 ​ 使用JUnit可以在提交测评机之前进行本地测试,可以通过构造数据计算预期结果比对程序输出结果判断程序的正确性,在第六次作业完成过程中涉及到很多计算还有精度的问题,使用JUnit在本地进行测试可以找出一些问题。编写JUnit过程中达到分支覆盖对于验证正确性也有很大帮助,第六次作业中继承关系中覆盖不同子类进行测试帮助我找到了一些bug。在当下阶段使用JUnit的不足在于构造数据过于简单,没有对边界条件进行测试(毕竟手搓复杂数据真的很难绷QWQ),总体来说在几次作业的迭代中,我通过使用JUnit实现了对于程序的本地测试,并且能够发现一些bug,使第一次提交至少通过数据点多了一些。 3.学习OOpre的心得体会 ​ 从面向过程到面向对象的编程思维的转变对于我来说还是有一些难度,我的代码中的很多编写也不够面向对象,导致方法行数爆炸,类行数爆炸,处理逻辑复杂。但是在几次迭代中,我也对面向对象有了基本的认识 理解面向对象的核心概念:面向对象编程是一种基于对象的思维方式。它的核心概念包括类、对象、封装、继承和多态。要想掌握面向对象编程,首先要理解这些概念的含义和关系 不断练习和总结经验:面向对象编程是一种需要不断实践和经验积累的编程方式。通过不断地练习和实践,才能更好地理解和应用面向对象编程的技巧。同时,还要及时总结经验教训,找到自己的不足之处并加以改进 阅读和理解优秀的面向对象代码:课程结束后学习优秀代码 4.对OOpre课程的简单建议 提高中测强度,尽量中测程度的数据过了就不要挂强测(强测挂了真的好压力) 指导书中部分内容可以进行细化,尤其是第七次作业的指导书,对于不同设计模式的解释可以再细致一点(?)

November 4, 2023 · 1 min · sudo

translate C recursive function into MIPS

Translate C recursive function into MIPS ​ 如何规范合理地把C语言中的递归函数翻译成MIPS assembly?这个问题令我头疼了一天,翻了网上很多教程总是感觉说的很浅,或者是重复着用MIPS编写计算阶乘的例子,找不到合适的教程对于一个迷茫的新手来说是一件非常绝望的事,经过我求助身边的大佬们,大佬们的一些分享,让我逐渐明白编写中的一些要点,并总结出一些编写规则。 一.C中的递归函数 ​ 对于编写MIPS程序,我们一般是先写出对应的C代码,再一句句翻译成MIPS语言。递归函数也是函数,从函数类型上看,应该有无返回值和有返回值这两种粗浅的大类,其中无返回值是一种值得注意的类型。 1.无返回值类型 ​ 在无返回值的递归函数中,我们往往只能看见递归层次中的return,但是实际上无返回值类型的返回语句可以省略,即程序运行结束时的return;可以省略,在翻译时,题目可能就会使用省略这一种写法,需要注意这个“隐藏”的return并在MIPS中自行编写。例如如i下C程序 1 2 3 4 5 6 7 8 9 10 #include<stdio.h> void print() { printf("hello world!"); (return;) } int main() { print(); } 2.有返回值类型 ​ 有返回值类型常见的为int等,有返回值类型的函数必须“显式“地说明出返回值,如果有分支,则在每个分支中都需要进行返回值的说明,这一类函数可以明显地看出哪里需要return对于编写比较友好,例如计算阶乘的示例 ...

October 22, 2023 · 5 min · sudo

P2课下&&MIPS常用宏定义

P2课下&&常用MIPS宏定义 ​ MIPS是一门非常灵活的语言,在编写过程中可以非常直观地感受到对内存的操作(虽然我编写MIPS都是照着C语言一句一句翻译),但也正因为是这样,使得MIPS编码看起来有一点繁琐,我们可以使用自己定义的宏macro进行代码风格的改善,将可以复用的代码抽离出来,类似于C语言中的函数。 1. MIPS——macro ​ MIPS中宏定义的方法是 //无参数宏定义 .macro name //statement .end_macro //有参数宏定义 .macro name(%parameter1,%parameter2,...) //statement .end_macro 1. end .macro end li $v0,10 syscall .end_macro 2. readinteger .macro readinteger(%des) li $v0,5 syscall move %des,$v0 .end_macro 3. printinteger .macro printinterger(%src) li $v0,1 move $a0,%src syscall .end_macro 4. printstr .macro printstr(%src) li $v0,4 la $v0,%src syscall .end_macro 5. 函数调用push .macro push(%src) sw %src,0($sp) subi ge$sp,$sp,4 .end_macro 6. 函数调用pop .macro pop(%des) addi $sp,$sp,4 lw %des,0($sp) .end_macro 7.一维数组计算地址偏移量 .macro getVectorAddress(%des,%col) sll %des,%col,2 .end_macro 8.二维数组计算地址偏移量 .macro getMatrixAddress(%des,%i,%j) multu %i,col(矩阵的列数) mflo %des add %des,%des,%j sll %des,%des,2 .end_macro 注意:一定是乘上矩阵的列数!QAQ之前乘了矩阵的行数还是看内存发现的bug ...

October 20, 2023 · 1 min · sudo

OOpre_HW_5:常见bug分析

OOpre_HW_5:常见bug分析 ​ 本次作业的任务比较简单,对课程组给出的代码进行debug,只有中测,让我这种挂了强测的鼠鼠好欣慰。 一.输入解析类错误 ​ 常见的有scanner一类的函数,我们需要注意的无非以下几个函数的功能 1 2 3 1. scanner.next(); //读取下一个字符串 2. scanner.nextint();//读取下一个数字 3. scanner.nextLine();//读取下一行字符串 ​ 作业中出现了几次使用scanner.nextLine()方法读取下一个字符串的错误,这种错误还是比较明显的。 二.深克隆与浅克隆 ​ 在我的作业代码中,对于深克隆和浅克隆共出现了一个功能中的两次错误,即克隆小队的操作和克隆士兵的操作。 1.浅克隆 ​ 浅克隆即只对对象的引用进行克隆,换句话说是创建出来新的一个指针,与原对象指向相同的一块内存空间。本质上两个引用指向的是同一个实例,一个指针对对象进行修改,另一个指针进行访问时就会体现出这种修改。例如: 1 2 Team team1 = new Team(123456,"dqr"); Team team2 = team1; 2.深克隆 ​ 深克隆不仅要创建出新的引用,还要开辟出新的内存空间,本质上就是一个新的变量,只不过变量的构造方法中传入参数是需要被克隆的对象的参数。 1 2 Team team1 = new Team(123456,"dqr"); Team team2 = new Team(team1.getID(),team1.getName()); 三.相等比较 ==/equals ? ==是比较两个引用是否是同一个对象 equals为内容比较,比如名字,咒语等等 四.遍历容器:迭代器删除 ​ 对于容器中的元素删除,我们可能经常会用到“边遍历边删除的操作” ...

October 18, 2023 · 1 min · sudo

OOpre_HW_4强测修复&&代码架构重构

HW_4强测修复&&代码重构 ​ **OOpre_HW_4是我打的最快的一次!**然而在代码风格上却不够面向对象,而且代码业务逻辑上有问题,没有成功通过强测(只得了33分),在挂掉强测之后,我痛定思痛,决定先重构代码架构再进行逻辑修复。 一.代码架构 ​ 上图是课程组推荐的代码架构,回顾我的第一版代码,主要有以下两个问题: 输入解析逻辑放在main类中,导致main代码冗长 没有对fightlog进行建类,而是将fightlog作为附属于adventure的数据处理,导致代码结构耦合复杂 ​ 经过一晚上的代码构想和助教的交流,我将代码架构修改为下图: ​ 在这次的代码逻辑中,我将fightlog视作一个个与adventure同级的个体建类(这个是最重要的思想,想了好久),fightlog中存储战斗日志的模式,攻击时间、攻击者的名字,被攻击者的名字ArrayList,这里需要注意,对于ArrayList<String> attackedname,应当分情况存储 mode == 1,此时attackedname == null mode == 2,此时attackedname中只有一个元素 mode == 3,此时attackedname中包含所有被攻击者的元素 二.bug修复 1.正则表达式修复 ​ 正则表达式出错使得战斗日志输入解析错误,导致后续从二维数组中读取时出现NullPointerException,这就是强测第一次的报错,只能说第一篇博客发早了,传播了错误的正则表达式。下面附上通过强测的正则表达式 1 2 3 Pattern p = Pattern.compile("(\\d{4}/\\d{2})-([^@#-]+)-([^@#-]+)"); Pattern p1 = Pattern.compile("(\\d{4}/\\d{2})-([^@#-]+)@([^@#-]+)-([^@#-]+)"); Pattern p2 = Pattern.compile("(\\d{4}/\\d{2})-([^@#-]+)@#-([^@#-]+)"); 2.对于携带概念的再纠正 ​ 我们知道,在第三次作业中,我对于携带的处理是为每个物品设置一个becarreid属性,在后续的处理中,如“使用”等操作,都需要进行是否“携带”概念的判断,在这次作业中,我发现了上次强测没有测出来的bug,OP9()中对于同名装备进行替换时,没有判断是否携带,下面附上改正后代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public void OP9(int i) { int advId = Integer.parseInt(inputInfo.get(i).get(1)); int equId = Integer.parseInt(inputInfo.get(i).get(2)); Adventure man = adventures.get(advId); Equipment equipment = null; ArrayList<Equipment> equipments = man.returnequ(man); for (Equipment item : equipments) { if (item.getID(item) == equId) { equipment = item; break; } } String name = equipment.getName(equipment); //检查当前想要携带的装备是否存在同名 如果有则进行替换 Equipment equipment1 = null; for (Equipment item : equipments) { if (item.getName(item).equals(name) && item.getBecarried(item)) { //已经被携带的同名装备 equipment1 = item; break; } } if (equipment1 != null) { equipment1.reset(equipment1); } equipment.set(equipment); } ​ 需要注意的是&&item.getBecarried(item),这种错误我出现了两次了,下次一定要注意,名字符合的同时要判断是否携带。 ...

October 18, 2023 · 1 min · sudo

P1上机日志

P1上机——verilog完成部件设计以及状态机 ​ 永远在周一晚上上机之前才是我学计组效率最高的时候 ​ ——地球人 一.三段式状态机的设计 ​ 在上机之前我心血来潮想要把自己编写状态机的代码风格从一直以来的一段式更新为三段式,在看了网上好多有的没的真真假假对对错错的写法之后,我决定转头求助蒋老师和曾老师,果然得到了靠谱的答案,并成功应用到晚上的上机中QAQ。 ​ 分段思路:就如同状态机的设计图,有三个模块:状态转移逻辑(组合逻辑),状态存储(时序逻辑),输出逻辑(组合逻辑),在verilog中分段实现即可,逻辑更加清晰。具体来说可以写成always-always-assign这种形式,当然最后一段也可以写成always. ​ 下面给出示例代码: ​ Moore型 输出逻辑中对状态进行判断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 module moore_example( input in; input clk; input rst_n; output check; ); localparam s0 = 4'b0001; localparam s1 = 4'b0010; localparam s2 = 4'b0100; localparam s3 = 4'b1000; reg [3:0] state; reg [3:0] n_state; //状态存储逻辑 always@(posedge clk or negedge rst_b)begin if(!rst_n)begin state <= s0; end else begin state <= n_state; end end //状态转移逻辑 一般来说两层case嵌套逻辑更加清楚 always@(*)begin case(state)begin s0: begin case(in)begin 1'b1:n_state = s1; 1'b0:n_state = s0; end end //省略其他部分 default: n_state = s0; end end //输出逻辑 assign check = (state == s3)?1'b1:1'b0; ​ Mealy型 输出逻辑中对当前状态和输入进行判断 ...

October 16, 2023 · 3 min · sudo

verilog编写电路中的时序逻辑与组合逻辑分离

P1_L1_voter_plus时序逻辑与组合逻辑的分模块编写&&阻塞赋值、非阻塞赋值 一.问题提出 ​ 在完成P1_L1_voter_plus一题时,由于需要对输入中1的位数进行统计,我第一次使用到for循环语句来统计输入中1的个数,原码类似于下例。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 module test_for ( input [7:0] in, input clk, input rst, output reg [7:0] out ); integer i; always @(posedge clk or posedge rst) begin : test if(rst) begin out <= 8'b0; end else begin for(i=0;i<8;i=i+1) begin if(in[i]) out <= out + 1'b1; end end end endmodule ​ 此段代码并不能成功实现在一个时钟周期内实现统计个数的功能。仿真如下: ...

October 15, 2023 · 3 min · sudo

verilog

verilog(P1课前复习) 长文,做好战斗准备! 1.同步复位与异步复位 1.同步复位 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 //复位信号高电平有效 always@(posedge clk) begin if(clr) begin status <= 4'b0000; end else begin //statement end end //复位信号低电平有效 always@(posedge clk) begin if(!clr) begin status <= 4'b0000; end else begin //statement end end 2.异步复位 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 //复位信号高电平有效 always@(posedge clk or posedge clr) begin if(clr) begin status <= 4'b0000; end else begin //statement end end //复位信号低电平有效 always@(posedge clk or negedge clr) begin if(!clr) begin status <= 4'b0000; end else begin //statement end end ​ 异步复位将clr加入敏感符号列表 ...

October 14, 2023 · 5 min · sudo

P1_L1_BlockChecker

语句块模拟检查 P1_L1_BlockChecker 一.题目要求 1.题目描述 ​ 输入ASCII字母和空格,一个或多个连续出现的字母构成单词,单词不区分大小写,单词之间由一个或多个空格分隔开。检查工具检查自复位之后的输入中,begin和end是否能匹配。 注: 匹配的begin必须出现在end之前 一个begin只能匹配一个end 允许出现嵌套 出现不能按照规则匹配的begin或end,则匹配失败 保证在模块使用前进行复位 2.IO定义 信号名 方向 描述 clk I 时钟信号 reset I 异步复位信号 in[7:0] I 当前输入的ASCII码 result O 当前输入能否完成begin和end匹配 注意:输出result为“当前”的判断结果,即随着状态更新 2.状态转移图 解释: 所有的单词都以空格表示结束对应回到S0状态 如果之前没有出现过begin就出现end,之后无论输入什么都会输出0,设置为单独的状态S10,这个状态是自环的,无论输入什么都会回到S10,并输出0. 3.verilog代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 module BlockChecker ( input wire clk, input wire reset, input wire [7:0] in, output reg result ); reg [31:0] begin_cnt; reg [15:0] status; `define S0 16'b0000_0000_0000_0001 `define S1 16'b0000_0000_0000_0010 `define S2 16'b0000_0000_0000_0100 `define S3 16'b0000_0000_0000_1000 `define S4 16'b0000_0000_0001_0000 `define S5 16'b0000_0000_0010_0000 `define S6 16'b0000_0000_0100_0000 `define S7 16'b0000_0000_1000_0000 `define S8 16'b0000_0001_0000_0000 `define S9 16'b0000_0010_0000_0000 `define S10 16'b0000_0100_0000_0000 always @(posedge clk or posedge reset) begin if(reset) begin begin_cnt <= 32'b0; status <= `S0; result <= 1'b1; end else begin case (status) `S0: //empty begin if(in == " ") begin status <= `S0; end else if (in == "e"||in == "E") begin status = `S7; end else if (in == "b"||in == "B") begin status = `S1; end else begin status = `S6; end end `S1: //b begin if(in == "e"||in == "E") begin status = `S2; end else if (in == " ") begin status <= `S0; end else begin status <= `S6; end end `S2: //be begin if(in == "g"||in == "G") begin status <= `S3; end else if (in == " ") begin status <= `S0; end else begin status <= `S6; end end `S3: //beg begin if(in == "i"||in == "I") begin status <= `S4; end else if (in == " ") begin status <= `S0; end else begin status <= `S6; end end `S4: //begi begin if(in == "n"||in == "N") begin status <= `S5; result <= 1'b0; end else if (in == " ") begin status <= `S0; end else begin status <= `S6; end end `S5: begin if(in == " ") begin status <= `S0; begin_cnt <= begin_cnt + 1; end else begin status <= `S6; if(begin_cnt == 32'b0000) begin result <= 1'b1; end else begin result <= result; end end end `S6: begin if(in == " ") begin status <= `S0; end else begin status <= `S6; end end `S7: begin if(in == "n"||in =="N") begin status = `S8; end else if (in == " ") begin status <= `S0; end else begin status <= `S6; end end `S8: begin if(in == "d"||in == "D") begin if(begin_cnt == 32'b0001) //只剩余一个begin 可以完成配对 begin status <= `S9; result <= 1'b1; end else begin status <= `S9; result <= 1'b0; end end else if (in == " ") begin status <= `S0; end else begin status <= `S6; end end `S9: begin if(in == " ") //确定匹配到的一定是end begin if(begin_cnt == 32'b0) //还没有出现过begin 这种情况无论后便出现什么都是0 begin status <= `S10; result <= 1'b0; end else if(begin_cnt == 32'b1) begin status <= `S0; result <= 1'b1; begin_cnt = 32'b0000; end else if(begin_cnt > 32'b1) begin status <= `S0; result <= 1'b0; begin_cnt = begin_cnt - 32'b0001; end end else //匹配到的不是end begin status <= `S6; if(begin_cnt==32'b0) begin result <= 1'b1; end else begin result <= 1'b0; end end end `S10: begin status <= `S10; result <= 1'b0; end default: begin status <= status; result <= result; end endcase end end endmodule 4.一个坑点 `S5: begin if(in == " ") begin status <= `S0; begin_cnt <= begin_cnt + 1; end else begin status <= `S6; if(begin_cnt == 32'b0000) begin result <= 1'b1; end else begin result <= result; end end end ​ 在进行单词begin的匹配时,需要对是否成功的匹配到begin做判断,一开始在两种之间来回改,后来才想到这是两种情况应该使用分支结构: ...

October 13, 2023 · 4 min · sudo

P1_L3_ALU && Verilog中的符号处理

P1_L3_ALU && Verilog中的符号处理 1.问题引入 AC? WA? ​ 在做P1课下提交中的6处理ALU时,我发现了一件怪事,两个看似只是添加了变量差别的程序居然跑起来一个能够通过测试,一个不能,下面附上AC代码和WA代码。 AC代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 module alu ( input [31:0] A, input [31:0] B, input [2:0] ALUOp, output [31:0] C ); wire [31:0] add = A + B; wire [31:0] sub = A - B; wire [31:0] with = A & B; wire [31:0] huo = A | B; wire [31:0] logic_shift = A >> B; wire [31:0] math_shift = $signed(A) >>> B; assign C = (ALUOp == 3'b000) ? add: (ALUOp == 3'b001) ? sub: (ALUOp == 3'b010) ? with: (ALUOp == 3'b011) ? huo: (ALUOp == 3'b100) ? logic_shift: (ALUOp == 3'b101) ? math_shift: 32'b000; endmodule WA代码 ...

October 12, 2023 · 2 min · sudo