作业集4~6的总结性Blog

一、前言
本阶段三次PTA作业围绕“数字电路模拟程序”展开迭代开发,从基础逻辑门模拟到复杂组合逻辑元件,再到子电路与异常处理。

知识点概览
作业4核心考察类的继承与多态、基础引脚封装、简单信号传播逻辑,实现与、或、非、异或、同或五种基础逻辑门;作业5聚焦接口设计、多引脚与多输出逻辑管理,新增复杂电路元件;作业6重点攻克嵌套逻辑递归解析、多优先级异常判断,新增子电路定义实例化与五类电路异常检测功能。

题量与难度
三次作业均为单道大题,层层递进、难度逐步升级,看的我简直烦死了,难度很大很抽象。

二、作业4:基础逻辑门模拟
2.1 题目要求
第一次作业要实现五种基本逻辑门:与门、或门、非门、异或门、同或门。但我一开始想得简单了些。

2.2 我的设计思路
我把五种门分别写成五个类,都继承一个抽象类Gate。Gate类里面定义了getId()、output()、isReady()这几个抽象方法。每个门类自己实现这些方法。
对于AndGate和OrGate,因为输入引脚数不固定,我用一个ArrayList来存输入值,用need变量记录需要多少个输入。输入值用add()方法加进去,当size()等于need时就认为就绪了。
对于NotGate,只有一个输入,用一个Integer变量存就行。XorGate和XnorGate有两个输入,用两个Integer变量存。
Main类负责读输入、创建门对象、建立连接、最后输出结果。我用了五个独立的ArrayList分别存五种门,想着这样分类清楚。

2.3 第一个问题:引脚号被忽略了
连接信息[A A(8)1-1 A(8)1-3 X5-2]中,A(8)1-1表示连到1号引脚,A(8)1-3表示连到3号引脚。我一开始图省事,只取了元件名A(8)1,后面的引脚号直接扔掉了。对于只有一个输入的非门来说没问题,但多输入门就不行了。所有输入都被当成1号引脚处理,顺序全乱套了。

2.4 第二个问题很严重,边读边算这是我犯的最大的错误。我的思路是:读到一行连接信息,立刻从输入信号中取值,或者从已经就绪的门中取值,然后直接设置给目标门。听起来好像没问题?但遇到这种情况就崩了:
第一行:[A(2)1-0 N1-1]
第二行:[A A(2)1-1]
第三行:[B A(2)1-2]
解析第一行的时候,A(2)1还没出现呢(在第二行才出现)。所以N1的输入设置不了。等到解析第二行和第三行的时候,A(2)1被创建了,也收到了A和B的值,但是第一行已经处理完了,N1永远等不到信号。
这就是两个测试点没过的原因。电路越复杂、连接顺序越乱,这个问题就越严重。
正确的做法应该是:先把所有输入读完存起来,把所有连接关系存起来,等全部读完了再统一计算。我改成这个方式之后,两个测试点就过了。

2.5 小结
类图
oop4

33cea2a8ca0f4577b6bcae3c428a8a7b
由SourceMontor分析,代码可读性较差,建议对代码的main方法进行拆分精简、减少代码嵌套,同时补充注释,降低整体代码冗余度与复杂度。

第一次作业让我学到了两点:第一,字符串处理要仔细,差一个字符结果就不一样;第二,解析和计算要分开,不能读一行就处理一行。输入顺序是不可控的,必须先完整解析再统一处理。

三、作业5:复杂组合逻辑元件
3.1 题目要求
第二次作业在第一次的基础上增加了四种新元件:三态门、译码器、数据选择器、数据分配器。这些元件的引脚比基本门复杂,有的有控制引脚,有的有多个输出引脚。

3.2 改进一:用Map存门
第一次作业我用五个独立的ArrayList分别存五种门,结果查找的时候要遍历所有List,代码很长很乱。这次我改用HashMap<String, Gate>,把门ID作为key,门对象作为value。需要找哪个门,直接get一下就拿到了。
这个改动看似简单,但让代码整洁了很多。而且后续作业中门越来越多,用Map管理的优势会更明显。

3.3 改进二:用Map存引脚值
第一次作业中,每个门自己存输入值。AndGate用ArrayList存,XorGate用两个变量存。这样每个门的存储方式不一样,处理起来很麻烦。
这次我统一用一个Map<Integer, Integer>来存输入值,key是引脚号,value是输入值。不管什么门,不管几个输入,都用这个方式。
比如A(2)1收到了引脚1的值1和引脚2的值0,Map里就是{1:1, 2:0}。判断是否就绪就看size()是否等于需要的引脚数。
这样处理的好处是输入顺序无所谓了。即使先读到引脚2的值,再读到引脚1的值,最终Map里都有,不影响结果。

3.4 小结
类图
oop5

image
同上,代码可维护性较差,建议对代码的较长主方法进行功能拆分,降低嵌套层级并补充关键业务注释。

第二次作业虽然元件变复杂了,但因为我在第一次作业的基础上改进了,这次作业满分通过,让我觉得第一次作业的教训没白吃。

四、作业6:子电路与异常处理
4.1 题目要求
第三次作业是最难的,加了两个大功能。
第一个是子电路。可以把一部分电路定义为一个子电路,然后在主电路中引用。子电路的格式是C编号开头,里面有INPUT、OUT、endc。子电路的引脚在主电路中用"子电路编号-引脚名"的方式引用。
第二个是异常检测。五种异常情况要检测出来,并且按优先级输出错误信息。优先级从高到低是:多个输出、无输入、无输出、输入输出顺序错误、输入信号冲突。

4.2 子电路的问题
子电路最麻烦的地方是名字冲突。子电路里可能有N1,主电路里也可能有N1,这两个重名了怎么办?
我的做法是在展开子电路的时候给里面的门加前缀。子电路C1里面的N1变成C1-N1,这样就不会和主电路的N1冲突了。子电路的输入引脚也加前缀,变成SUB_INPUT_1-A这样的形式。
主电路中[C1-A]要找到子电路C1的输入引脚A,然后连接到对应的SUB_INPUT_1-A上。这个查找逻辑看起来简单,但实际上很容易出错。
我遇到的问题有两个:
第一,子电路嵌套。C2里面引用了C1,展开C2的时候要先展开C1。我的代码虽然会递归调用展开方法,但是展开C1之后,C1的输出引脚映射没有正确传回来,导致C2里面的[C1-B B]找不到C1的输出值。
第二,主电路的输入信号和子电路的输入引脚容易混淆。主电路中有INPUT: X-1,子电路C1中也有INPUT: A。主电路中[X C1-A]要把X的值传给子电路的A,这个没问题。但我的代码在查找的时候,有时候会找到主电路的输入信号X,而不是子电路的输入引脚A,导致连接错误。
这两个问题导致好几个测试点过不了。我调试了很久,打印了很多中间变量,才慢慢找到问题所在。但即使到最终提交,嵌套子电路的处理仍然不够完善。

4.3 异常处理的问题
五种异常的检测优先级很明确:多个输出排第一,无输入排第二,无输出排第三,顺序错误排第四,信号冲突排第五。
我犯的错误是把信号冲突放在最前面检测了。结果某条连接既有多个输出又有信号冲突的时候,应该报"多个输出",我的程序报了"信号冲突"。
这个错误很隐蔽。因为如果只测试单种异常,我所有检测都是对的。但题目要求的是优先级,当多种异常同时存在时,只输出优先级最高的那种。
修复的办法是把检测逻辑按题目给的顺序排列。先检测是否有多个输出,如果有就直接返回;再检测无输入,如果有就直接返回;依此类推。前面检测到了就不管后面的了。
还有一个问题是"输入信号冲突"的检测时机。两个连接连到同一个输入引脚才算冲突,但我的程序在读第一条连接的时候还不知道第二条连接的存在。所以冲突检测不能在读的过程中做,要等所有连接都读完了再统一检查。

4.4 小结
类图
oop6

image
分析及同上,其中instantiateSubCircuit()方法复杂度达45,代码444行,最大嵌套深度8层,是代码臃肿、维护困难的主要原因,建议抽取子电路实例化与连接解析逻辑,以消除主方法代码臃肿问题。

第三次作业是我花时间最多的,也是问题最多的。子电路的嵌套展开、异常检测的优先级顺序、不同连接之间的冲突检测,这些都是我之前没有遇到过的复杂情况。最让我头疼的是调试。一个连接出错,可能原因有很多,每次都要调试很久才能定位问题。到最后提交还是有测试点没过,说明我对子电路嵌套展开的处理确实还有问题。如果还有时间,我会从展开逻辑入手,尽量把其余测试点调试成功。

五、踩坑总结
5.1 引脚号不能忽略
连接信息中的引脚号很重要,不能只取元件名。A(2)1-1和A(2)1-2虽然元件名相同,但是连的是不同的引脚,输入值不能混在一起。
5.2 解析和计算要分开
边读边算是最容易出错的。输入顺序不可控,必须先读完所有内容存起来,然后统一计算。
5.3 异常检测按优先级顺序
多种异常同时存在时,只输出优先级最高的那个。检测的时候要按优先级从高到低,找到一个就直接返回,不再继续检测。
5.4 子电路要避免重复展开
同一个子电路可能被多次引用,展开一次就够了。用一个集合记录已经展开过的子电路编号。

六、学到的内容
1.通过这三次作业,我对类的继承、抽象方法、Map和List的使用、字符串处理这些基础内容有了更深的体会。
2."边读边算"和"先读后算"是两种不同的处理思路。第一种看起来简单,但实际上问题很多。第二种虽然多了一个存储的步骤,但处理起来更稳健。这个思维方式不仅适用于这三次作业,以后遇到类似的也可以用。
3.最重要的教训是:先想清楚再写代码。作业6之所以反复失败,就是因为我一看到题目就开始写,没有先把子电路的展开逻辑梳理清楚。还有就是要多打印中间结果来调试。打印门的状态、连接的映射、输入信号的值,这些信息帮助我找到了很多错误。

七、需要继续学习的地方
通过本次阶段性复盘,我深知处理复杂问题要先理清逻辑流程、想好异常情况,再动手写代码。同时要努力提升自己的编程设计能力。

posted @ 2026-06-24 09:48  givemeagem  阅读(3)  评论(0)    收藏  举报