首页 > 编程语言 >结对项目:用Python实现四则运算

结对项目:用Python实现四则运算

时间:2023-09-24 22:44:37浏览次数:45  
标签:结对 denominator Python self 四则运算 operator answer expression stack

这个作业属于哪个课程 计科1/2班
这个作业要求在哪里 结对项目
这个作业的目标 实现一个自动生成小学四则运算题目的命令行程序

团队成员

姓名 学号
梁昊东 3121005000
李铭伟 3121004145

github链接https://github.com/e1ecb0t/e1ecb0t/tree/main/caculator

1. PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 60 40
·Estimate 估计这个任务需要多少时间 60 40
Development 开发 750 840
·Analysis 需求分析(包括学习新技术) 240 270
·Design Spec 生成设计文档 120 100
·Design Review 设计复审 30 40
·Coding Standard 代码规范(为目前的开发制定合适的规范) 30 30
·Design 具体设计 120 140
·Coding 具体编码 180 200
·Code Review 代码复审 30 60
Test 测试(自我测试,修改代码,提交修改) 200 240
·Reporting 报告 120 150
·Test Repor 报告测试 30 30
·Size Measurement 计算工作量 20 20
·Postmortem & Process Improvement Plan 事后总结,并提出过程改进计划 30 40
合计 1010 1120

2. 效能分析

cpu占用时间检测

1

可以看到程序主要耗时的部分为生成题目及答案部分,占cpu耗时的98.6%

内存占用检测

2

可以看到程序最大内存占用为0.6MiB

程序总耗时

3

3. 设计实现过程

111

随机生成算术表达式

(1) 在Arithmetic类中,随机生成运算符个数、操作数个数,并设置值域范围。

(2) 随机生成操作数列表和运算符列表。

(3) 构建表达式的中缀表达式,中间可能随机插入括号。

(4) 删除无用括号,返回表达式列表。

计算表达式

(1) 在Calculate类中,将中缀表达式转化为后缀表达式。

(2) 使用栈的方式计算后缀表达式:遍历后缀表达式,数字入栈,操作符时取栈顶两个元素进行运算,结果入栈,直到遍历完表达式。

(3) 返回最终计算结果。

生成多条随机表达式

(1) 在Generator类中,通过多进程实现表达式生成和IO写入并发。

(2) 一个进程随机生成表达式并放入队列。

(3) 另一个进程从队列中取出表达式,写入文件。

(4) 使用缓冲区方式批量写文件,避免IO频繁。

4. 代码说明

def mainfuction():

识别命令行参数,调用相应的函数

add_path = ''
# 识别并获取命令行参数
arg = arg_parse()

print("+----------------------------------------------------------+\n"
      "|                 欢迎使用四则运算算式生成器                    |\n"
      "+----------------------------------------------------------+\n"
      "|                1. -n:  输入并获取生成的题目数量               |\n"
      "|                2. -r:  输入并获取参与运算的最大值              |\n"
      "|                3. -e:  选择题目文件                         |\n"
      "|                4. -a:  选择答案文件                         |\n"
      "+----------------------------------------------------------+\n"
      )



# 否则使用命令行操作
try:
    # 生成题目及答案或者检查答案,两者不能同时进行
    if (arg.e or arg.a) and (arg.n or arg.r):
        raise IOError
    elif arg.a and arg.e:
        # 输入参数正确,执行检查代码部分
        inspect(os.path.join(current_path, arg.a[0]), os.path.join(current_path, arg.e[0]))
    elif arg.n and arg.r:
        # 输入参数正确,执行生成题目和答案文件Grade?.txt
        Generator(arg.n[0], arg.r[0]).multi_processor()
    else:
        print("帮助信息: 参数输入错误")
except Exception as e:
    print(e)

def inspect(answer_file, expression_file):
检查函数

# 正确错误列表序号
correct_seq = []
wrong_seq = []

try:
    # 读取文件
    with open(expression_file, 'r', encoding='utf-8') as fa:
        expression_content = fa.readlines()
    # 读取文件
    with open(answer_file, 'r', encoding='utf-8') as fb:
        answer_content = fb.readlines()

    # 由答案文件获取序号 再在运算式中找到相对应的题目计算答案 再比较
    # 获取列表
    for item_b in answer_content:

        # 当前答案的行数的序列号
        answer_sqe, answer = int(item_b.split('. ')[0]), item_b.split('. ')[1]

        # 找到对应的习题的行数
        expression = expression_content[answer_sqe - 1]

        # ###############################################
        # print(answer_sqe, expression)

        # 分割字符
        pattern = expression.strip().replace(' ', '').replace(' ', '').split('.')[1]
        pattern = list(filter(None, split(r'([()×÷+-])', pattern)))

        # 提取表达式并计算 如若正确存进
        if Calculate(pattern).cal_expression()[0].to_string() == answer.strip():
            correct_seq.append(answer_sqe)

    # 生成错误列表
    for item_a in expression_content:
        a_sqe = int(item_a.split('. ')[0])
        if a_sqe not in correct_seq:
            wrong_seq.append(a_sqe)

    # 保存结果
    save_inspect(correct_seq, wrong_seq)

except IOError:
    print('Failed to open file')
    return

class Calculate(object):
计算表达式结果:

def __init__(self, expression):
    self.expression = expression

# 分数加法 a1/b1 + a2/b2 = (a1b2 + a2b1)/b1b2
@staticmethod
def fraction_add(fra1, fra2):
    molecular = fra1.molecular * fra2.denominator + fra2.molecular * fra1.denominator
    denominator = fra1.denominator * fra2.denominator

    return Fraction(molecular, denominator)

# 分数减法 a1/b1 - a2/b2 = (a1b2 - a2b1)/b1b2
@staticmethod
def fraction_minus(fra1, fra2):
    molecular = fra1.molecular * fra2.denominator - fra2.molecular * fra1.denominator
    denominator = fra1.denominator * fra2.denominator

    return Fraction(molecular, denominator)

# 分数乘法 a1/b1 * a2/b2 = a1a2/b1b2
@staticmethod
def fraction_multiply(fra1, fra2):
    molecular = fra1.molecular * fra2.molecular
    denominator = fra1.denominator * fra2.denominator
    return Fraction(molecular, denominator)

# 分数除法 a1/b1 ÷ a2/b2 = a1b2/a2b1
@staticmethod
def fraction_divide(fra1, fra2):
    molecular = fra1.molecular * fra2.denominator
    denominator = fra1.denominator * fra2.molecular

    return Fraction(molecular, denominator)

# 基本运算选择器
def operate(self, num1, num2, operater):
    if not isinstance(num1, Fraction):
        num1 = Fraction(num1)
    if not isinstance(num2, Fraction):
        num2 = Fraction(num2)

    # 计算结果
    if operater == '+':
        return self.fraction_add(num1, num2)
    if operater == '-':
        return self.fraction_minus(num1, num2)
    if operater == '×':
        return self.fraction_multiply(num1, num2)
    if operater == '÷':
        return self.fraction_divide(num1, num2)

def generate_postfix_expression(self):
    # 运算符栈
    operator_stack = []
    # 后缀栈
    postfix_stack = []

    for element in self.expression:
        # 如果是操作数则添加
        if element not in operators:
            postfix_stack.append(element)
        # 如果是运算符则按优先级
        elif element in operator.values():
            # 运算符栈为空,或者栈顶为(,则压栈
            if not operator_stack or operator_stack[-1] == '(':
                operator_stack.append(element)
            # 若当前运算符优先级大于运算符栈顶,则压栈
            elif priority[element] >= priority[operator_stack[-1]]:
                operator_stack.append(element)
            # 否则弹栈并压入后缀队列直到优先级大于栈顶或空栈
            else:
                while operator_stack and priority[element] < priority[operator_stack[-1]]:
                    postfix_stack.append(operator_stack.pop())
                operator_stack.append(element)

        # 如果遇到括号
        else:
            # 若为左括号直接压入运算符栈
            if element == '(':
                operator_stack.append(element)
            # 否则弹栈并压入后缀队列直到遇到左括号
            else:
                while operator_stack[-1] != '(':
                    postfix_stack.append(operator_stack.pop())
                operator_stack.pop()

    while operator_stack:
        postfix_stack.append(operator_stack.pop())

    return postfix_stack

# 计算表达式(运算过程出现负数,或者除数为0,返回False,否则返回Fraction类)
def cal_expression(self):
    # 生成后缀表达式
    expressions_result = self.generate_postfix_expression()
    # 存储阶段性结果
    stage_results = []

    # 使用list作为栈来计算
    calculate_stack = []

    # 后缀遍历
    for element in expressions_result:
        # 若是数字则入栈, 操作符则将栈顶两个元素出栈
        if element not in operators:
            calculate_stack.append(element)
        else:
            # 操作数
            num1 = calculate_stack.pop()
            # 操作数
            num2 = calculate_stack.pop()

            # 除数不能为0
            if num1 == "0" and element == '÷':
                return [False, []]

            # 结果
            result = self.operate(num2, num1, element)

            if result.denominator == 0 or '-' in result.to_string():
                return [False, []]

            stage_results.append(result.to_string())

            # 结果入栈
            calculate_stack.append(result)

    # 返回结果
    return [calculate_stack[0], stage_results]

class Generator(object):
随机生成表达式

def __init__(self, num=10, domain=100, order=0):
    self.order = order
    # 表达式个数
    self.expression_num = num
    # 表达式集
    self.expressions = []
    # 答案集
    self.answers = []
    # 值域
    self.domain = domain
    """
    [ [expression1], [expression2], ... ]
    """
    # 单次写入的缓冲区
    self.buffer_expression = []
    self.buffer_answer = []
    self.buffer_domain = 1000 if num >= 10000 else 100
    """    
    用答案作为索引构建的字典,
    {
        "1'2/2": [
        [[压入的数字], [操作数], [运算符]],
        [[压入的数字], [操作数], [运算符]],
        ...
    ]
    }
    """
    self.no_repeat_dict = {}

# 检查重复
def judge_repeat(self, answer, test_sign):
    for expression_sign in self.no_repeat_dict[answer]:
        # 记录相同的个数
        same_num = 0

        for i in range(3):
            if collections.Counter(expression_sign[i]) == collections.Counter(test_sign[i]):
                same_num += 1
        # 如果中间结果、操作数、运算符均相等,则为重复
        if same_num == 3:
            return False
    return True

# 表达式-生产
def expression_generator(self, queue):

    while self.expression_num > 0:

        [expression, operand_list, operator_list] = Arithmetic(self.domain).create_arithmetic()
        [answer, stage_results] = Calculate(expression).cal_expression()

        # 计算错误
        if answer:
            # 如果该答案不存在字典中,则新建该键值对,否则判断重复,若重复则不添加表达式
            stringify_answer = answer.to_string()
            if stringify_answer in self.no_repeat_dict:
                if self.judge_repeat(stringify_answer, [stage_results, operand_list, operator_list]):
                    self.no_repeat_dict[stringify_answer].append([stage_results, operand_list, operator_list])
                    self.expression_num -= 1
                    res = [expression, answer, self.expression_num]
                    queue.put(res)
            else:
                self.no_repeat_dict[stringify_answer] = [[stage_results, operand_list, operator_list]]
                self.expression_num -= 1
                res = [expression, answer, self.expression_num]
                queue.put(res)

# 表达式-消费
def io_operation(self, queue):

    index = 0
    while True:

        index += 1
        res = queue.get()
        if res is not None:

            answer = f"{index}. {res[1].to_string()}"
            expression_remain_num = res[2]

            expression_str = str(index) + ". "

            for item in res[0]:
                if item in operator.values():
                    expression_str += " " + item + " "
                else:
                    expression_str += item

            # 存入缓冲区
            self.buffer_expression.append(expression_str)
            self.buffer_answer.append(answer)
            # 缓冲区满100个时 写文件
            if index % self.buffer_domain == 0 and expression_remain_num > self.buffer_domain:
                # 交由I/O操作函数
                save_exercise(self.buffer_expression, self.order)
                save_answer(self.buffer_answer, self.order)
                # 清空buffer
                self.buffer_expression.clear()
                self.buffer_answer.clear()
        else:
            # 交由I/O操作函数
            save_exercise(self.buffer_expression, self.order)
            save_answer(self.buffer_answer, self.order)
            # 清空buffer
            self.buffer_expression.clear()
            self.buffer_answer.clear()
            # exit()
            break

# 调用接口 生成多条题目
def multi_processor(self):
    start_time = time.time()

    queue = multiprocessing.Queue()
    producer = multiprocessing.Process(target=self.expression_generator, args=(queue,))
    consumer = multiprocessing.Process(target=self.io_operation, args=(queue,))

    producer.start()

    consumer.start()

    producer.join()
    queue.put(None)

    ene_time = time.time()
    print(f"\nBuffer size:{self.buffer_domain}, time cost: {ene_time - start_time}\n")

5. 测试运行

生成题目Exercises.txt:

1. 3'1/4 - 1
2. (3'2/3 × 2'4/7) ÷ 3/6
3. 0 × 5'4/6 - (0 × 0)
4. 2'3/5 ÷ 6
5. 2'6/8 × 7
6. 4'3/5 - 0
7. (1'2/5 × 4'1/2) - (1/2 × 7/9)
8. 8'2/7 - 3/6
9. 2/6 - 0
10. (5'2/7 ÷ 2'2/6) + 9'6/9 + 6'6/7

题目对应结果Answer.txt:

1. 2'1/4
2. 18'6/7
3. 0
4. 13/30
5. 19'1/4
6. 4'3/5
7. 5'41/45
8. 7'11/14
9. 1/3
10. 18'116/147

检查结果Grade.txt:

Correct: 10 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Wrong: 0 []
Accuracy: 100.0%

6. 项目小结

通过紧密合作,我们完成了本次作业

  • 我们每隔一段时间会讨论项目的进展、遇到的挑战以及下一步的计划,确保了我们的想法和信息能够尽快同步
  • 我们进行了明确的分工, 充分发挥了各自的优势
  • 在合作中,我们对出现的问题进行统一讨论,有时候我没能解决的问题,对方能解决。我们彼此都能想到对方想不到的点,这样互相完善,比起一个人来说,效率大大提升了
  • 在合作项目中,合理把控进度对于按时按质完成项目来说至关重要。我们制定了详细的计划,确保项目按时完成。我们密切监控进展,并及时调整计划。这帮助我们保证了项目最终的完成度。
  • 在合作过程中,遇到的困难和挑战应该由所有的的团队成员面对,不能因为不是自己负责的模块就推卸责任

标签:结对,denominator,Python,self,四则运算,operator,answer,expression,stack
From: https://www.cnblogs.com/lhdBlogs/p/17726862.html

相关文章

  • python教程:调用svn status命令对提交的文件进行add状态过滤(只保存新增加的文件)
    需求说明编写一段python程序,用于对svnadd状态的文件进行过滤,并用列表对这些文件进行保存。代码实现以下是一个示例的Python程序,用于对SVN的svnstatus命令中状态为“A”(新增)的文件进行过滤,并将它们存储在一个列表中:importsubprocessdefget_added_files():added_fi......
  • python教程:解决报错:ERROR: THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIRE
    从以下两种途径来解决。清除缓存这个错误通常表示安装的软件包与要求文件中的哈希值不匹配。这可能是由于要求文件被更改或软件包被篡改引起的。为了解决这个问题,你可以尝试以下几个步骤:清理缓存:运行以下命令清理pip缓存:pipcachepurge```更新要求文件:如果你更新了软件包的版本......
  • python.exe get-pip.py安装失败
    装pip要用get-pip.py来安装,但安装时还要下载whl文件,如果系统中没有设置国内镜像源,从国外下,会经常失败。原因:大部分失败都是因为网络问题才失败的。解决方法:使用国内源,在C:\Users\你的用户名\pip文件夹下,把以下内容保存到pip.ini里。[global]index-url=http://mir......
  • Python分享
    Python斗地主不完全代码importrandom#定义扑克牌的花色和大小suits=["♠","♥","♦","♣"]ranks=["A","2","3","4","5","6","7","8","9",......
  • python列表
    追加appemd("")插入insert(位置,"")合并extend("")嵌套names.insert(2,[1,2,3])names[2][1]2删除del()pop() 默认删除最后一个,并返回删除值,可以指定,但是要输入索引remove("")  指定元素名,其中有重复会从左边开始删第一个clear()  清空修改names[0]="你好"......
  • 21python实现简单的消息队列
      frommultiprocessingimportQueue'''q=Queue(num)若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接收的消息数量没有上限(直到内存的尽头)。函数也是队列的初始化。Queue.qsize()返回当前队列包含的消息数量。Queue.empty()如果队列为空,返回T......
  • 用Python实现的本地美食和餐饮业SEO策略
     当谈到本地美食和餐饮业的SEO(搜索引擎优化)策略时,Python是一种强大的编程语言,可以帮助我们自动化和优化各种任务。在这篇文章中,我将介绍一些使用Python实现的本地美食和餐饮业SEO策略的方法。 1.网站优化(WebsiteOptimization): -使用Python的网页解析库(如BeautifulSoup)来......
  • Python-day17
    1、查看保留字importkeywordprint(keyword.kwlist)2、小数相加fromdecimalimportDecimalprint(Decimal('1.1')+Decimal('1.1'))3、数据类型转换name='cecilia'age=1print('我叫{},今年{}岁'.format(name,age))print(f'我叫{name},今年{age}岁'......
  • Python教程(14)——Python函数的入门学习
    函数是什么?在编程中,函数是一段可重用的代码块,用于完成特定任务或执行特定操作。它可以接输入参数并返回一个值或执行一系列操作。函数可以帮助程序员将代码模块化,提高代码的可读性和可维护性。函数通常包括以下组成部分:函数名:用于标识函数,并可以通过函数名调用执行该函数。参......
  • python列表入门学习
    Python是一个非常强大且易于学习的编程语言,而列表(list)是Python中最常用的数据结构之一。无论你是初学者还是经验丰富的开发者,理解和掌握Python列表的使用都是非常重要的。1.什么是列表?列表是一个有序的元素集合,可以容纳多个值,这些值可以是任何数据类型。#定义一个空列表empt......