自动生成四则运算题目(python实现)
项目分析
项目仓库: 地址
需求
- 仅包含四则运算
- 结果不能为负数
- 数字大小在 100 以内
- 支持真分数运算
设计实现过程及代码说明
项目文件结构如下:
模块 | 功能 |
---|---|
main.py | 主函数(表达式生成, 表达式的求解) |
mainTest.py | 测试函数(单元测试) |
1. 分析与设计
本设计设计栈的使用, 逆波兰表达式(后缀表达式)
表达式式生成
仔细分析有如下特点:
- 运算符的个数比运算数少一个
- 被除数不能为 0
具体实现步骤
- 利用 Python 的字符串来存储表达式
- 随机生成一个运算数
- 再随机选择一个四则运算符
- 重复步骤 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万次的分析结果如下: