生成四则运算小程序

第二次结对作业

作业题目

我们选择的题目是“一个小学四则运算自动生成程序”

题目要求

  • 能够自动生成四则运算练习题

  • 可以定制题目数量

  • 用户可以选择运算符

  • 用户设置最大数(如十以内、百以内等)

  • 用户选择是否有括号、是否有小数

  • 用户选择输出方式(如输出到文件、打印机等)

  • 最好能提供图形用户界面(根据自己能力选做,以完成上述功能为主)

作业中的角色

驾驶员

导航员为王艳东,博客地址

代码仓库地址

仓库地址

exe文件地址

作业思路

首先,先建立一个合适的数据结构,用来表达四则运算这种算式。应该有数,算式符号和括号这些元素的具体值和位置等属性。我决定使用数组这个数据结构来存储这些信息。

其次,需要考虑如何生成这样一个描述算式的数据结构。需要先随机确定有几个数参与运算,然后根据给定的数的最大值确定具体每个数的大小。我们还需要随机确定每两个数之间的运算符号是什么。接着,我们应该随机确定应该有几个括号和括号的位置。最后,根据符号的约束规则,随机生成这些符号和其位置。

最后,用一个函数,将生成好的数据结构信息,“翻译”为算式去展示。

具体代码

生成运算式的类

# -*- 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界面,进行怎样的编程流程。在编码的过程中,提出了很多建议。在编码遇到困难的时候给了我很大信心和鼓励。

我的导航员长善于发现程序中的问题,在编程的过程中,能够及时的把问题提出来,或者提出自己的疑问。在这个过程中,我们进行了互动,互相弥补自己的不足。在互相的交流中,我发现了更多的编程乐趣。在解决相关问题时,通过讨论、查阅文档、搜索信息等方式,能很快的找到解决方案。所以编程的时候,提升了编程信心。

后期测试的过程中,对程序的各部分,进行了充分的测试,思考了使用怎样的测试样例对程序进行测试。及时反馈问题,然后我们共同改进。在上述括号的问题中,我们先是提出了各自的解决方法,然后我们一起讨论,使用哪种方法更加好一些。我觉得这些,交流和沟通,对提升大家的编程信心,熟悉具体代码和业务,促进大家感情等方面都有积极作用。

结对编程或许是熟悉业务,学习交流的好方式。

总结

不可否认,结对编程能够提高一部分编程信心,更有利于编出高质量的代码,另外在遇到问题时能够从更多角度思考问题的解决方案。

但是,很让人担心它的效率。个人感觉适用于一些高质量,高可靠性代码的地方。

posted @ 2019-05-02 19:21  左键已坏  阅读(523)  评论(0编辑  收藏  举报