python 实现小学四则运算
小学四则运算
1. 项目要求
- 随机生成小学内的四则运算式子,不包含负数
- 支持真分数运算
2. PSP流程表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 30 |
Development | 开发 | 300 | 420 |
Analysis | 需求分析 (包括学习新技术) | 30 | 45 |
Coding Standard | 代码规范 (制定合适的规范) | 20 | 25 |
Design | 具体设计 | 30 | 40 |
Coding | 具体编码 | 30 | 60 |
Code Review | 代码复审 | 30 | 30 |
Test | 测试 | 40 | 60 |
Reporting | 报告 | 20 | 30 |
合计 | 520 | 740 |
3. 解题思路描述
该问题总体可分为两部分: 第一部分生成算式, 第二部分计算算式
3.1 第一部分
首先,随机生成操作数和运算符,通过参数设置操作数的大小上限、运算符的数量以及是否含分数。
接着,在算式中插入括号,括号插在减法的减数位置上或者乘除法附近的加减子算式中。
最后,将算式拼接起来。
3.2 第二部分
通过栈先将算式转为后缀表达式即逆波兰式,之后再计算出算式的结果,结果保留分数形式。
4. 代码说明
4.1 项目文件结构
模块 | 功能 |
---|---|
main.py | 主函数 |
stack.py | 栈实现 |
formula_library.py | 存放算式和正确答案 |
methods.py | 包含美化命令行界面、获取参数、输出算式、核对答案 |
generate_ari.py | 生成算式 |
calculator.py | 计算算式结果,含中缀表达式转后缀表达式 |
核心模块为generate_ari类和calculator类
4.2 部分模块代码
generate_ari类
点击查看代码
import re
import random
from fractions import Fraction
class GenerateAri():
'''生成算术表达式'''
def __init__(self, maxNum=10, optNum=1, hasFraction=False):
self.init_operators(optNum, hasFraction)
self.init_operands(maxNum, hasFraction)
self.merge_expression()
def init_operand(self, maxNum, hasFraction):
'''生成随机数'''
if hasFraction:
while True:
numerator = random.randint(1, maxNum)
denominator = random.randint(1, maxNum)
if numerator != denominator:
break
return Fraction(numerator, denominator)
else:
return random.randint(2, maxNum)
def init_operators(self, optNum, hasFraction):
'''生成操作符'''
if hasFraction:
self.opts = [random.choice(['+', '-', '×', '÷']) for i in range(optNum)]
else:
self.opts = [random.choice(['+', '-', '×']) for i in range(optNum)]
self.opts.append('=')
self.insert_bracket()
def init_operands(self, maxNum, hasFraction):
'''生成操作数'''
self.nums = [str(self.init_operand(maxNum, hasFraction)) for i in range(len(self.opts))]
def insert_bracket(self):
'''插入括号'''
optsNum = len(self.opts)
leftBrackets = [''] * optsNum
rightBrackets = [''] * optsNum
begin, index = 0, 0
strOpt = ''.join(self.opts)
while begin < optsNum-1:
if self.opts[begin] == '+':
begin += 1
continue
search = None
if begin-index > 1:
if self.opts[begin] in ['×', '÷']:
search = re.search(r'[+|-]', strOpt[index:begin][::-1])
if search:
rightBrackets[begin] = ')'
leftInd = random.randint(index, begin-search.start()-1)
leftBrackets[leftInd] = '('
index = begin
if index >= optsNum-2:
break
search = None
if self.opts[begin] in ['×', '÷', '-']:
if self.opts[begin] == '÷':
search = re.search(r'[×|÷|+|-]', strOpt[begin+1:])
else:
search = re.search(r'[+|-]', strOpt[begin+1:])
if search:
leftBrackets[begin+1] = '('
rightInd = random.randint(begin+2+search.start(), optsNum-1)
rightBrackets[rightInd] = ')'
index = rightInd
begin = index
else:
begin += 1
self.brackets = [leftBrackets, rightBrackets]
def merge_expression(self):
'''合并表达式'''
self.expression = [
''.join([i, j, k, l])
for i, j, k, l in zip(self.brackets[0], self.nums, self.brackets[1], self.opts)
]
self.expression = ''.join(self.expression)
for _ in set(re.findall(r'[=|×|÷|+|-]', self.expression)):
self.expression = self.expression.replace(_, ' {} '.format(_))
calculator类
点击查看代码
import stack
from fractions import Fraction
class Calculator:
def __init__(self):
self.operators = ['+', '-', '×', '÷', '(', ')', '=']
self.priority = {'+': 1, '-': 1, '×': 2, '÷': 2, '(': 99}
def getResult(self):
'''计算结果'''
valueStack = []
for item in self.suffixExp:
if item in self.operators:
x2 = valueStack.pop()
x1 = valueStack.pop()
result = self.calculate(x1, x2, item)
if result < 0:
return '-1'
valueStack.append(str(result))
else:
valueStack.append(item)
return valueStack[0]
def calculate(self, x1, x2, opt):
'''操作数出栈进行计算'''
if '/' in x1 or '/' in x2:
x1 = Fraction(x1)
x2 = Fraction(x2)
else:
x1 = eval(x1)
x2 = eval(x2)
if opt == '+':
return x1 + x2
elif opt == '-':
return x1 - x2
elif opt == '×':
return x1 * x2
elif opt == '÷':
return Fraction(x1 / x2).limit_denominator() if x2 != 0 else -1
else:
return -1
def infix2suffix(self, ari):
'''中缀表达式转后缀表达式'''
self.suffixExp = []
optStack = stack.Stack()
exps = list(zip(ari.brackets[0], ari.nums, ari.brackets[1], ari.opts))
for exp in exps:
for element in exp:
if element == '=':
while not optStack.is_empty():
popChar = optStack.pop()
if popChar != '(':
self.suffixExp.append(popChar)
break
if element in self.operators:
if optStack.is_empty():
optStack.push(element)
else:
if element == ')':
while not optStack.is_empty() and optStack.peek() != '(':
popChar = optStack.pop()
self.suffixExp.append(popChar)
if optStack.size() > 0:
optStack.pop()
elif self.priority[optStack.peek()] < self.priority[element]:
optStack.push(element)
else:
if optStack.peek() == '(':
optStack.push(element)
else:
while not optStack.is_empty() and self.priority[optStack.peek()] >= self.priority[element] and optStack.peek() != '(':
popChar = optStack.pop()
self.suffixExp.append(popChar)
optStack.push(element)
else:
if element != '':
self.suffixExp.append(element)
methods模块
点击查看代码
import sys
import time
import datetime
from fractions import Fraction
def entrance():
'''程序开始提示语'''
print('{:-^60}'.format(''))
print('{:^60}'.format(''))
print('{:^55}'.format('四 则 运 算(不 含 负 数)'))
print('{:^60}'.format(''))
print('{:-^60}'.format(''))
for word in ' 欢迎使用【四则运算】v1.0':
sys.stdout.write(word)
sys.stdout.flush()
time.sleep(0.2)
time.sleep(1)
sys.stdout.write('\r{:<50}\r'.format(''))
sys.stdout.flush()
def getParameters():
'''获取用户输入的算式参数'''
for word in ' 请输入您希望的算式参数:':
sys.stdout.write(word)
sys.stdout.flush()
time.sleep(0.2)
time.sleep(0.5)
print()
while True:
print('{:^60}'.format('*'*30))
try:
formulaNum = int(input('{0:15} {1:>15} | '.format(' ', '算式数量(>=1)')))
assert formulaNum >= 1
maxNum = int(input('{0:15} {1:>15} | '.format(' ', '数值上限(>=10)')))
assert maxNum >= 10
operatorNum = int(input('{0:15} {1:>14} | '.format(' ', '运算符个数(>=1)')))
assert operatorNum >= 1
hasFraction = int(input('{0:15} {1:>13} | '.format(' ', '是否包含分数(0, 1)')))
assert hasFraction == 0 or hasFraction == 1
print('{:^60}'.format('*'*30))
except AssertionError:
print('{:^60}'.format('*'*30))
print(' ❌ 输入错误, 请输入正确范围下的数值')
print('{:-^60}'.format(''))
print(' 请输入您希望的算式参数:')
except ValueError:
print('{:^60}'.format('*'*30))
print(' ❌ 输入错误, 请输入正确的数值')
print('{:-^60}'.format(''))
print(' 请输入您希望的算式参数:')
else:
print('{:-^60}'.format(''))
break
return formulaNum, maxNum, operatorNum, hasFraction
def getOutputMode():
'''获取输出模式'''
while True:
print('{:^51}'.format('请选择算式输出模式'))
print('{0}\n{1}'.format(' 1、普通模式', ' 2、问答模式'))
try:
mode = int(input(' 请选择: '))
assert 1 <= mode <= 2
except AssertionError:
print(' ❌ 输入错误, 请输入正确范围下的数值')
print('{:-^60}'.format(''))
except ValueError:
print(' ❌ 输入错误, 请输入正确的数值')
print('{:-^60}'.format(''))
else:
print('{:-^60}'.format(''))
break
return mode
def checkAnswer(userAnswer, library, titleNum=0):
'''核对答案'''
if isinstance(userAnswer, list):
checks = []
for result in userAnswer:
try:
result = str(Fraction(result).limit_denominator())
checks = checks + [True] if result == library.results[titleNum] else checks + [False]
except Exception:
checks = checks + [False]
finally:
titleNum += 1
return checks
else:
try:
check = True
userAnswer = str(Fraction(userAnswer).limit_denominator())
if userAnswer != library.results[titleNum]:
check = False
except ValueError:
check = False
finally:
return check
def outputFormula(mode, library):
'''输出算式'''
if mode == 1:
userAnswers = []
start = datetime.datetime.now()
for num, exp in enumerate(library.formulas):
userAnswers = userAnswers + [input('({}) {}'.format(num+1, exp))]
end = datetime.datetime.now()
checks = checkAnswer(userAnswers, library)
errorIndex = [ind for ind, value in enumerate(checks) if value is False]
if len(errorIndex) > 0:
print(' ❌ 错误 {} 题: '.format(len(errorIndex)), end='')
for ind in errorIndex:
print('({})'.format(ind+1), end=' ')
print()
print(' ⭕ 正确答案: ', end='')
for ind in errorIndex:
print('({}) {}'.format(ind+1, library.results[ind]), end=' ')
print()
else:
print(' ✅ 恭喜你全部答对了')
print()
print('正确率: {:.2f}%'.format(100*float((library.formulaNum()-len(errorIndex))/library.formulaNum())))
print('耗时: {}'.format(end-start))
else:
errorCount = 0
start = datetime.datetime.now()
for num, exp in enumerate(library.formulas):
userAnswer = input('({}) {}'.format(num+1, exp))
if checkAnswer(userAnswer, library, num):
print('⭕ 回答正确!!! 正确答案是: {}'.format(library.results[num]))
else:
errorCount += 1
print('❌ 回答错误!!! 正确答案是: {}'.format(library.results[num]))
end = datetime.datetime.now()
print()
print('正确率: {:.2f}%'.format(100*float((library.formulaNum()-errorCount)/library.formulaNum())))
print('耗时: {}'.format(end-start))
5. 测试运行
5.1 正确运行
5.2 错误输入