生成四则运算小程序
第二次结对作业
作业题目
我们选择的题目是“一个小学四则运算自动生成程序”
题目要求
能够自动生成四则运算练习题
可以定制题目数量
用户可以选择运算符
用户设置最大数(如十以内、百以内等)
用户选择是否有括号、是否有小数
用户选择输出方式(如输出到文件、打印机等)
最好能提供图形用户界面(根据自己能力选做,以完成上述功能为主)
作业中的角色
驾驶员
导航员为王艳东,博客地址
代码仓库地址
作业思路
首先,先建立一个合适的数据结构,用来表达四则运算这种算式。应该有数,算式符号和括号这些元素的具体值和位置等属性。我决定使用数组这个数据结构来存储这些信息。
其次,需要考虑如何生成这样一个描述算式的数据结构。需要先随机确定有几个数参与运算,然后根据给定的数的最大值确定具体每个数的大小。我们还需要随机确定每两个数之间的运算符号是什么。接着,我们应该随机确定应该有几个括号和括号的位置。最后,根据符号的约束规则,随机生成这些符号和其位置。
最后,用一个函数,将生成好的数据结构信息,“翻译”为算式去展示。
具体代码
生成运算式的类
# -*- coding: utf-8 -*- """ @File : Calculation.py @Time : 2019/4/27 8:43 @Contact : lirookyoujian@gmail.com @Modify Time @Author @Version @Desciption ------------ ------- -------- ----------- 2019/4/27 8:43 zhenfeilee 1.0 None """ import random # 一个数据结构 class stru(): def __init__(self): self.num_count = 0 # 数的个数 self.num_result_count = 0 self.num_arr = [] # 数的数组 self.bra_arr = [] # 括号的数组 self.brackets = 0 # 括号的数目 self.sym = [] # 符号的数组 self.sym_arr = [] # 符号的存储 self.sym_count = 0 # 符号的数目 self.result = 0 class ger(): def gen_que(self, count, max_num, dec, sym, bra, fileout, num_cal_count): ''' :param count:要出的题目数 :param max_num:最大数 :param dec:是否有小数 :param sym:运算符号 :param bra:是否有括号 :param fileout:输出方式 :param num_cal_count:最多几个数运算 :return: ''' result = "" mystru = stru() # 实例化一个stru类 for i in range(count): mystru.__init__() mystru.num_count = random.randint(2, num_cal_count) mystru.num_result_count = mystru.num_count mystru.sym = sym mystru.sym_count = mystru.num_count - 1 if dec: # 有小数的情况下 for i in range(mystru.num_count): if random.randint(0,1): mystru.num_arr.append(round(random.uniform(1, max_num), 1)) # 取一位精度 else: mystru.num_arr.append(random.randint(1, max_num)) else: # 整数情况 for i in range(mystru.num_count): mystru.num_arr.append(random.randint(1, max_num)) for i in range(mystru.sym_count): # 生成运算符号 mystru.sym_arr.append(random.choice(sym)) if bra: # 生成括号 self.genet_bra(mystru) result = result + self.print_que(mystru) + "\n" # print(self.print_que(mystru) + str(mystru.bra_arr) + "\n") return result def print_que(self, stru): """ 根据stru生成一个算式,stru中有必要的原料 :param stru:输入一个算式结构 :return:str """ que = "" for i in range(stru.num_count + 1): if i == 0 and (self.judege_bra(stru, 1, 0)): que = que + "(" * self.judege_bra(stru, 1, 0) + str(stru.num_arr[0]) elif i == 0 and (not self.judege_bra(stru, 1, 0)): que = que + str(stru.num_arr[0]) elif i == stru.num_count and (self.judege_bra(stru, stru.num_count, 1)): que = que + ")" * self.judege_bra(stru, stru.num_count, 1) + "=" elif i == stru.num_count and (not self.judege_bra(stru, stru.num_count, 1)): que = que + "=" else: if self.judege_bra(stru, i, 1): que = que + ")" * self.judege_bra(stru, i, 1) que = que + stru.sym_arr[i - 1] if self.judege_bra(stru, i + 1, 0): que = que + "(" * self.judege_bra(stru, i + 1, 0) que = que + str(stru.num_arr[i]) return que def judege_bra(self, stru, posi, left_right): """ 判断括号是否存在 :param stru: :param posi: :param left_right:位于一个数的左边0,还是右边1 :return:int 代表出现了几次 """ count = 0 for i in range(stru.brackets): if stru.bra_arr[i][left_right] == posi: count = count + 1 return count def genet_bra(self, stru): """ 生成括号数组 :param stru:输入一个stru结构 :return: """ brackets = stru.num_count - 2 stru.brackets = random.randint(0, brackets) # 括号数目 while True: stru.bra_arr = [] for i in range(stru.brackets): while True: temp_list = random.sample([j for j in range(1, stru.num_count + 1)], 2) # 随机选两个数作为括号 temp_list.sort() # 对列表排序 if (temp_list[0] == 1) and (temp_list[1] == stru.num_count): # 括号无意义 continue elif self.list_repeat(stru.bra_arr, temp_list): # 括号查重 continue else: stru.bra_arr.append(temp_list) break if self.jude_bra_valid(stru): break def list_repeat(self, list1, list2): """ 括号去重 :param list1:stru中的括号数组 :param list2: :return: """ for i in range(len(list1)): if (list1[i][0] == list2[0]) and (list1[i][1] == list2[1]): return True if (list1[i][1] == list2[0]) or (list1[i][0] == list2[1]): return True return False def jude_bra_valid(self, stru): """ 判定括号是否合法 :param stru: :return:bool """ if stru.brackets == 1: return True else: stru.bra_arr.sort(key=self.get_list_subtract) # 对数组排序 stru.bra_arr.sort(key=self.get_list_fir) # 再次排序 for i in range(stru.brackets - 1): for j in range(i + 1, stru.brackets): if (stru.bra_arr[i][1] > stru.bra_arr[j][0]) and ( stru.bra_arr[i][0] < stru.bra_arr[j][0]) and ( stru.bra_arr[i][1] < stru.bra_arr[j][1]): return False return True def get_list_subtract(self, lst): """ 返回数组中第二个和第一个的差 :param lst: :return: """ return lst[1] - lst[0] def get_list_fir(self, lst): """ 返回数组中的第一个值 :param lst: :return: """ return lst[0]
主控程序类
# -*- coding: utf-8 -*- """ @File : deal.py @Time : 2019/4/27 8:50 @Contact : lirookyoujian@gmail.com @Modify Time @Author @Version @Desciption ------------ ------- -------- ----------- 2019/4/27 8:50 zhenfeilee 1.0 None """ from PyQt5 import QtCore, QtGui, QtWidgets from untitled import Ui_MainWindow from child import Ui_Dialog from PyQt5.QtWidgets import QMessageBox import sys from Calculation import ger import info class Mywindow(QtWidgets.QMainWindow, Ui_MainWindow): def __init__(self): """ 完成GUI初始化工作,绑定槽 """ super(Mywindow, self).__init__() self.setupUi(self) # 初始化GUI控件 self.horizontalSlider.valueChanged[int].connect(self.horizon_changeValue) self.horizontalSlider_2.valueChanged[int].connect(self.horizon2_changeValue) self.pushButton.clicked.connect(self.pushbutton_clich) self.child_dia = Mydialog() def pushbutton_clich(self): try: if self.check_inpue_valid(): count = int(self.lineEdit_2.text()) max_num = self.horizontalSlider.value() dec = self.checkBox.isChecked() bar = self.checkBox_2.isChecked() out = self.radioButton.isChecked() sym = self.get_sym() num_cal_count = self.horizontalSlider_2.value() mygerne = ger() # 实例化一个ger对象 pri_str = mygerne.gen_que(count, max_num, dec, sym, bar, out, num_cal_count) # 调用生成算式的函数 info.STR = pri_str if out: # 输出到文件 self.fileout(pri_str) QMessageBox.information(self, "提示信息", "已生产文件,文件名为‘que.txt’", QMessageBox.Yes) else: # 屏幕显示 self.child_dia.set_textbro() self.child_dia.exec_() except Exception as e: print(e) def fileout(self, t_str): with open("que.txt", "w", encoding="utf8") as f: f.write(t_str) # 检查输入是否合法 def check_inpue_valid(self): if self.lineEdit_2.text() == '' or int(self.lineEdit_2.text()) == 0: QMessageBox.warning(self, "警告信息", "题目数量数据不合法", QMessageBox.Yes) return False if self.horizontalSlider.value() == 0: QMessageBox.warning(self, "警告信息", "最大数不合法", QMessageBox.Yes) return False if not self.get_sym(): QMessageBox.warning(self, "警告信息", "至少选择一个运算法则", QMessageBox.Yes) return False return True # 获取选择的运算符 def get_sym(self): sym_list = [] if self.checkBox_3.isChecked(): sym_list.append('+') if self.checkBox_4.isChecked(): sym_list.append('÷') if self.checkBox_5.isChecked(): sym_list.append('-') if self.checkBox_6.isChecked(): sym_list.append('×') return sym_list def horizon_changeValue(self, value): self.label_3.setText(str(value)) def horizon2_changeValue(self, value): self.label_5.setText(str(value)) # 子窗口 class Mydialog(QtWidgets.QDialog, Ui_Dialog): def __init__(self): super(Mydialog, self).__init__() self.setupUi(self) def set_textbro(self): self.textBrowser.setLineWrapMode(QtWidgets.QTextEdit.NoWrap) # textBrowser要想正常显示水平滚动条,必须将lineWrapMode设置为nowrap self.textBrowser.setText(info.STR) # 设置文本浏览器 if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) myshow = Mywindow() myshow.show() sys.exit(app.exec_())
所需工具及环境
- python3.6
- PYQT5
- pyqt-tool
- PyInstaller
运行效果
经过测试,程序运行很符合预期,算式的生成较为合适。
程序主页面如下
输出到屏幕
输出到文件
文件已生成
错误警告信息
产生小数
文件打包为exe程序
作业中遇到的问题
主要是括号的生成问题。因为采用随机生成的方式,所以需要对产生的括号进行合法性检验,在这个过程中需要寻找一个合适的括号产生法则。因为一开始的考虑不周,所以开始的时候,产生括号的时候会出现"(10)"这种情况。通过不断的改进,对括号的约束法则的探索,越来越深入,改进了之前的错误。
程序可以继续改进的地方
-
在括号的生成上,采用的随机生成然后筛选的算法不够高效。
-
可以设定计算式结果
-
美化UI
欢迎大家fork,不断改进,使之成为一个实用的程序。
导航员的作用
在设计程序的算法和编程的过程中,导航员起到了很大作用。我们一起讨论使用什么样的数据结构存储,设计什么样的UI界面,进行怎样的编程流程。在编码的过程中,提出了很多建议。在编码遇到困难的时候给了我很大信心和鼓励。
我的导航员长善于发现程序中的问题,在编程的过程中,能够及时的把问题提出来,或者提出自己的疑问。在这个过程中,我们进行了互动,互相弥补自己的不足。在互相的交流中,我发现了更多的编程乐趣。在解决相关问题时,通过讨论、查阅文档、搜索信息等方式,能很快的找到解决方案。所以编程的时候,提升了编程信心。
后期测试的过程中,对程序的各部分,进行了充分的测试,思考了使用怎样的测试样例对程序进行测试。及时反馈问题,然后我们共同改进。在上述括号的问题中,我们先是提出了各自的解决方法,然后我们一起讨论,使用哪种方法更加好一些。我觉得这些,交流和沟通,对提升大家的编程信心,熟悉具体代码和业务,促进大家感情等方面都有积极作用。
结对编程或许是熟悉业务,学习交流的好方式。
总结
不可否认,结对编程能够提高一部分编程信心,更有利于编出高质量的代码,另外在遇到问题时能够从更多角度思考问题的解决方案。
但是,很让人担心它的效率。个人感觉适用于一些高质量,高可靠性代码的地方。