自动生成四则运算题目(python实现)

自动生成四则运算题目(python实现)

项目分析

项目仓库: 地址

需求

  1. 仅包含四则运算
  2. 结果不能为负数
  3. 数字大小在 100 以内
  4. 支持真分数运算

设计实现过程及代码说明

项目文件结构如下:

模块 功能
main.py 主函数(表达式生成, 表达式的求解)
mainTest.py 测试函数(单元测试)

1. 分析与设计

本设计设计栈的使用, 逆波兰表达式(后缀表达式)

表达式式生成

仔细分析有如下特点:

  • 运算符的个数比运算数少一个
  • 被除数不能为 0

具体实现步骤

  1. 利用 Python 的字符串来存储表达式
  2. 随机生成一个运算数
  3. 再随机选择一个四则运算符
  4. 重复步骤 1 和 2

为了美观和操作方便, 表达式中运算符和运算数使用空格隔开

求解表达式

中缀表达式转换为后缀表达式, 再进行求值

具体代码实现

表达式生成代码


def makeFormula(upperLimit=100, fraction=False) -> str:
    if fraction:
        upperLimit = 20
        count = randint(4, 8)
    else:
        count = randint(1, 3)
    build = ""
    number1 = randint(1, upperLimit)
    build += str(number1)

    for i in range(count):
        if fraction and (i+1) % 2:
            operation = 3
        elif fraction:
            operation = randint(1, 2)
        else:
            operation = randint(1, 3)
        number2 = randint(1, upperLimit)
        op = ' ' + OP[operation] + ' '
        build += op + str(number2)

    return build

中缀表达式转换为后缀表达式


def getPostfixExpression(infixExpr: str) -> List[str]:
    # 记录操作符优先级
    prec = {'*': 3, '/': 3, '+': 2, '-': 2, '(': 1}
    postfixList = []
    operatorStack = []
    # 以空格分割表达式, 并转为字符数组
    tokenList = infixExpr.split()

    # 中缀表达式转换为后缀表达式
    for token in tokenList:
        if token in "+-*/":
            while operatorStack and \
                    prec[operatorStack[-1]] >= prec[token]:
                postfixList.append(operatorStack.pop())
            operatorStack.append(token)
        elif token == '(':
            operatorStack.append(token)
        elif token == ')':
            topToken = operatorStack.pop()
            while topToken != '(':
                postfixList.append(topToken)
                topToken = operatorStack.pop()
        else:
            postfixList.append(token)
    while operatorStack:
        postfixList.append(operatorStack.pop())

    return postfixList

求解后缀表达式


def solvingPostfixExpression(postfixList: List[str]):
    operandStack = []

    # 计算后缀表达式
    for token in postfixList:
        if token in "+-*/":
            operand2 = operandStack.pop()
            operand1 = operandStack.pop()
            try:
                result = doMath(token, operand1, operand2)
                operandStack.append(result)
            except:
                return "ERROR: Dividend cannot be 0"
        else:
            operandStack.append(int(token))

    return operandStack.pop()
    
def doMath(op: str, number1, number2):
    if op == '+':
        return number1 + number2
    elif op == '-':
        return number1 - number2
    elif op == '*':
        return number1 * number2
    else:
        return Fraction(number1, number2)

项目测试

主要使用 unittest 进行单元测试

具体代码如下


class MyTestCase(unittest.TestCase):
    def test_solvingPostfixExpression_valid(self):
        test = solvingPostfixExpression(
            ['3', '5', '/', '1', '6', '/', '-'])
        result = Fraction(13, 30)
        self.assertEqual(test, result)

        test = solvingPostfixExpression(
            ['3', '15', '*', '20', '4', '/', '-'])
        result = 40
        self.assertEqual(test, result)

    def test_solvingPostfixExpression_invalid(self):
        test = solvingPostfixExpression(['2', '0', '/'])
        result = "ERROR: Dividend cannot be 0"
        self.assertEqual(test, result)

        test = solvingPostfixExpression(
            ['6', '5', '10', '2', '/', '-', '/'])
        result = "ERROR: Dividend cannot be 0"
        self.assertEqual(test, result)

    def test_getPostfixExpression(self):
        test = getPostfixExpression("22 / 2")
        result = ["22", "2", "/"]
        self.assertEqual(test, result)

        test = getPostfixExpression("38 - 5 * 6")
        result = ['38', '5', '6', '*', '-']
        self.assertEqual(test, result)

        test = getPostfixExpression("( 16 - 9 ) * 6")
        result = ['16', '9', '-', '6', '*']
        self.assertEqual(test, result)


if __name__ == '__main__':
    unittest.main()

性能分析

直接使用 Pycharm 自带的性能测试工具进行性能分析
运行100万次的分析结果如下:

项目运行结果截图

posted @ 2020-09-21 01:01  康诚嘉士  阅读(615)  评论(0编辑  收藏  举报