结对编程 - 队友代码分析

纵观

  此次项目,我们都是基于 python 来实现的,所以代码看起来十分亲切。

  说实话,好久没看别人的代码了,当我拿到搭档的代码时,不禁发出感叹:“居然没有面向对象编程!”。细看之后,其实就差封装一下嘛,整体代码写的优雅而不失端庄,含蓄而又不失奔放,平淡中显示出不凡的编程功底,可谓是逻辑严谨,条条有理,字字珠玑,句句经典,代码风格确实登得大雅之堂,是我辈应领悟之典范 (๑•̀ㅂ•́)و✧

工程文件

  接下来我解读一下搭档的项目,他本来打算用一个模块(文件)实现,在本人强烈建议下分了模块写,整个工程由 4 个文件组成:

  • Account_information.xlsx:保存用户信息的表格,有用户等级、用户名、密码三个字段
  • Formula.py:编写了生成式子会用到的方法
  • Utils.py:有点潦草,里面只有一个返回指定格式的日期和时间的方法 - -
  • main.py:交互层,也是要运行主文件,编写的是一些和用户交互的方法,包括登录、选择、查重、生成/保存试卷

测试结果

  提供了一个可执行文件  main.exe 可以直接测试程序,结果是实现了所有指定的功能 ୧(๑•̀◡•́๑)

优点

  • 输出的提示好看,交互良好

  虽然规定了只能用控制台展示,但就凭 cmd 的交互提示也比我的好看哎。

  

  • 容错性强,还算是靠谱的

  比如我不按照格式输入,也不会使程序报错而停止运行。

  

   

  • 编码规范,可读性强

  毕竟这代码不是只给自己看的,所以搭档的程序中变量、方法的命名严格按照编程规范,见名知意。代码块之间的空行和缩进也严格遵守  python  的编码风格,让人看了如沐春风。

  • 代码低耦合,高内聚,重用性好

  每个文件中一个方法只实现一个功能;且与其他模块不相交;方便修改和找  bug;基本上没有重复冗余的代码,抽象程度适当,每个方法体都不算庞大。

  • 生成式子的算法新颖可靠,值得学习

  生成随机算式的思路为:首先生成一个随机的合法逆波兰表达式,再将逆波兰表达式转化为中缀表达式。由于生成逆波兰表达式是随机的,而一个逆波兰表达式唯一映射一个中缀表达式,也就保证了生成的中缀表达式是随机的,且很好地解决了括号(位置与数量)的随机问题。

  生成逆波兰表达式:依据后缀表达式的计算流程(遍历表达式,遇到数字压栈,遇到操作符取栈顶两个数字出栈计算后压栈)易知,任意逆波兰表达式的前两项必定为数字,最后一项必定为操作符,且计算过程中遇到符号至少保证栈中要有两个数字。

  所以满足以下要求即可生成随机的合法逆波兰表达式:

  1. 初始化状态下,随机生成两个数字,并为最后的操作符预留一个位置

  2. 用一个变量记录此时栈中的元素个数,每次生成一个数字,栈中个数加一,每次生成一个操作符,栈中个数减一。每一次生成都需要判断此时栈中元素个数,如果小于2,则只能随机生成一个操作数;如果大于2,则先随机选择是生成操作数还是操作符。

  3. 在最后位置上随机生成操作符。

 1 def InversePolish():
 2     """
 3     生成随机的合法逆波兰表达式
 4     """
 5     if g_operand_num == 1:
 6         operand = random.randint(1, 100)
 7         g_inverse_polish.append(operand)
 8         
 9     elif g_operand_num == 2:
10         for _ in range(2):
11             operand = random.randint(1, 100)
12             g_inverse_polish.append(operand)
13 
14         operator_index = random.randint(0, 3)
15         g_inverse_polish.append(g_operator_lib[operator_index])
16 
17     else:
18         while g_operand_num > 0 or g_operator_num > 1:
19 
20             if g_stack_num < 2:
21                 RandomOperand()
22             else:
23                 type_random = random.randint(0, 1)
24                 if type_random == 0 and g_operand_num > 0:
25                     RandomOperand()
26                 else:
27                     RandomOperator()
28 
29         RandomOperator()

   逆波兰表达式转化为中缀表达式:算法思想是模拟后缀表达式的计算过程(但过程中入栈的不是计算结果而是计算表达式),为每个操作符和入栈的表达式记录一个优先级,依据优先级判断表达式是否需要添加括号。

缺点

  人无完人,代码也肯定会存在一些缺点,下面来盘一盘。

  • 这个程序是面向过程的,没有使用到类;因此倘若变更需求,代码需要改动的可能较多。
  • 检查密码是否正确这里有点潦草,就是判断密码是否等于 “123”,不是很通用。
  • 保存试卷采用的是生成一个式子就打开文件保存一次,这会使得整个程序文件  I/O 次数过多,其实可以先存在数据结构中,生成完指定数量的式子再一次读入文件。
  • 查重那里的逻辑不够好:项目中查重的设计是,生成式子过程中如果查到有式子重复了就把已生成的式子都作废了,重新生成整个试卷,这会使得一个式子重复导致很大的代价,然而只需要重新生成一个不重的式子就行了。
  • 生成的式子的一些不足之处:由于生成初中和高中式子中用到的符号都是随机生成的,可能不包含指定有的操作符:1.生成初中题目时不能保证一定包含一个中级符号;2.生成高中题目时不能保证一定含有一个高级符号,且不会包含中级符号。
  • 用户名存在于外部本地文件,可能造成一定的依赖,存于云端数据库中能够减少依赖,这也是我们下一步结对编程想要改进的地方。(还有 Utils.py 文件略微有凑工程之嫌疑,就不多说了吧...)

感想

  总的来看,整个程序还是稍稍有些许不足,我觉得使用面向对象思维会更好,然后还可以继续分模块实现,比如将用户和用户管理分开定义到一个类中,这样使得代码看起来更加有结构,层次分明。

  不过也让我在对自己的项目中的某些地方的实现有了新的认识,让自己发现了新大陆,每一个人的代码思路与风格都不相同,但我们都能从别人的代码中学习到很多,进而实现对自己代码思路与风格的改进,希望以后写的代码能更加符合规范。

  执笔至此,不再多言,前路漫漫,下次再聊。

 

 

posted @ 2020-09-30 13:48  这次一定  阅读(96)  评论(0)    收藏  举报