20220511数据结构绿书读书笔记






20220511
Life类的定义
每个Life对象都需要包含一个矩形数组,用1表示存活的细胞,0表示死亡的细胞。这样表示的好处是,如果要计算一个特定细胞的邻居,我们只需要把相邻的加起来就行了。因为每代的更新中都需要重复的计算每个细胞的邻居数量,所以Life类应当包含一个成员方法neighborCount来做这个任务。因为client的代码不需要知道这个方法,所以设置成private

计算邻居
可以预见(这说法特别像数学证明里的显然可得,然而初学者设计程序并不会有这种经验,只能是踩过坑才知道),如果需要边界细胞的邻居需要更多的判断条件,我们可以做一些优化,让数组的实际大小比定义的大一些,周围留出一圈,这样既避免了从0开始数的麻烦,也避免掉了多余的判断,让所有单元格计算邻居的时候可以用相同的代码

更新矩阵
更新的动作是相当明确的,我们首先计算一个新的矩阵用于记录更新后的数据,然后完整的把新的数据复制回原地即可(直接用新的不好吗?)
更新过程的算法比较容易,我们遍历非边界的地方,然后判断存活邻居的个数,实际上只需要判断2个和3个就行,其他的无论如何都是死亡。
这里需要注意下,如果需求让你打印死亡或存活的原因,就不能这么更新了。这也是很多搞算法的同学容易忽略的,算法题往往只需要一个正确的结果,记录这个结果一个bit位或者一个int就能搞定,而真实的需求往往会要求记录更多的信息。在这个例子里,可能的需求就是记录下每个细胞存活和死亡的代数和原因,后面可能还有可能查出相同复活和死亡原因的细胞分布,或者统计每个细胞存活的代数。什么?你没记?你怎么能不记呢?不记到时候加需求你怎么做啊?hhhhhh
程序的运行应当尽可能的留痕以供人工查阅或后期处理,记录尽可能原始的数据,而不是像AI神经网络一样输出一个莫名其妙大概率正确的结果而不知道为什么
如果考虑实际需求,那么数组的类型就不能是int了,还得再套个对象,记录当前细胞每代更新对自己的影响结果这种原始数据备用,可以用于计算它的出生代数、死亡代数、存活实际等其他数据,以防产品后面加需求。这是一个来自刚工作几年没少踩坑的开发的经验之谈,信不信由你

输入和输出
程序一般设计上是给很多人使用的,输入和输出的方法通常是最长的,输入的程序需要完整检查用户出入的正确性和一致性,输出要合理的格式化,要好好考虑什么应该输出什么不应该

编程原则:
保持你的输出和输出再不同的方法里,来让他们可以被轻易的修改,让你的系统有一定的custom tailored

提示语打印程序是一个非常简单的程序,只负责打印提示

测试用例drivers
可以写一小段程序给一个方法提供一些特定的输入,单独测试一个方法的运行是否正常

程序跟踪和debug
一个很常用的工具就是打印语句,详细的打印程序执行时所处的位置和相关变量的状态,生产环境一般用日志框架来处理,有的公司会有完整的日志收集、搜索、监控平台来做这件事情,自己写着玩直接print就好
脚手架代码也可以在debug的时候提供额外的帮助,用过之后记得删掉就好。当你的程序出现奇怪的错误时,脚手架代码可以放在1个或2个主程序的关键位置,用于快速确认问题出现的具体行数。

程序测试原则
测试数据的质量远比测试数据的数量要重要
程序测试可以证明bug的存在型,但无法证明bug不存在

程序测试方法
黑盒测试
使用普通数据、特殊的正常数据、边界附近的数据、非法的数据(参考测试工程师进酒吧)

白盒测试
分析程序的的每个分支,对每个执行路径都进行测试,大型程序种这种测试并不现实,一般选择性测试主要的执行路径

Ticking-Box测试
不知道怎么翻译,翻译成随缘测试吧hhh
写完了让用户自己去用吧,出了问题一定是你不会用,才不是bug呢

20220511结束,耗时一个半小时,明天继续看程序的评估


0 Comments
Leave a Reply