Skip to content

Instantly share code, notes, and snippets.

@myuanz
Created November 15, 2020 10:49
Show Gist options
  • Save myuanz/d7f43062d3ad2fe746469c27026147d6 to your computer and use it in GitHub Desktop.
Save myuanz/d7f43062d3ad2fe746469c27026147d6 to your computer and use it in GitHub Desktop.
简单的 Python 带括号四则运算计算器 / Simple Python elementary arithmetic calculator
def merge(ops: list[str], values: list[int], final_merger=False):
priority = {'*': 1, '/': 1, '+': 2, '-': 2, '$': 9999}
while True:
if len(ops) >= 2:
last, last_last = [ops.pop() for _ in range(2)]
if final_merger:
last_last, last = last, last_last
elif len(ops) >= 1 and len(values) >= 2:
last_last, last = ops.pop(), '$'
else:
break
if priority[last_last] <= priority[last] or final_merger:
right, left = values.pop(), values.pop()
values.append(eval(f'{left} {last_last} {right}'))
last != '$' and ops.append(last)
else:
ops.append(last_last)
last != '$' and ops.append(last)
break
def less_eval(exp: str) -> tuple[list[str], list[int], str]:
values = []
ops = []
i = 0
waiting_next_number = False
while i < len(exp):
char = exp[i]
if char in '0123456789':
value = int(char)
while i+1 < len(exp) and (new_char := exp[i+1]) in '0123456789':
value = value * 10 + int(new_char)
i += 1
values.append(value)
elif char in '+-*/':
ops.append(char)
merge(ops, values)
elif char == '(':
new_ops, new_values, new_exp = less_eval(exp[i + 1:])
values.append(new_values[0])
i += len(new_exp) + 1
elif char == ')':
merge(ops, values, final_merger=True)
return ops, values, exp[:i]
waiting_next_number = False if waiting_next_number else True
i += 1
merge(ops, values, final_merger=True)
return ops, values, exp[:i]
def test_less_eval():
assert less_eval('0 - 2')[:2] == ([], [-2])
assert less_eval('2 * 2')[:2] == ([], [4])
assert less_eval('2 / 2')[:2] == ([], [1])
assert less_eval('20 / 2')[:2] == ([], [10])
assert less_eval('20 / 20')[:2] == ([], [1])
assert less_eval('20 / 200')[:2] == ([], [0.1])
assert less_eval('(1 + 2) * 3')[:2] == ([], [9])
assert less_eval('(1 + 2) * 3 + 2 / 2')[:2] == ([], [10])
assert less_eval('(1 + 2) * (3 + 2 * (2 + 1)) / 2')[:2] == ([], [13.5])
assert less_eval('0 - 2 -3 - 4 -5 ')[:2] == ([], [-14])
assert less_eval('1 + 2 * 3')[:2] == ([], [7])
assert less_eval('(1 + 2) * (3 + 2 * (2 + 1 + 1)) / 2')[:2] == ([], [16.5])
def test_with_random():
from time import time
import random
for i in range(100):
new_exp = []
while len(new_exp) < 50:
new_exp.append(random.choice('123456789'))
new_exp.append(random.choice('+-*/'))
new_exp.append(random.choice('123456789'))
for j in range(random.randint(5, 15)):
left_index = random.randint(0, len(new_exp) - 3)
while new_exp[left_index] not in '0123456789':
left_index = random.randint(0, len(new_exp) - 3)
new_exp.insert(left_index, '(')
right_index = random.randint(left_index, len(new_exp) - 1)
while new_exp[right_index] not in '0123456789':
right_index = random.randint(left_index + 1, len(new_exp) - 1)
new_exp.insert(right_index + 1, ')')
try:
exp = "".join(new_exp)
t = time()
v1 = eval(exp)
t1 = time() - t
v2 = less_eval(exp)[1][0]
t2 = time() - (t + t1)
assert v1 == v2
print(t1, t2)
except ZeroDivisionError:
print(exp, 'ZeroDivisionError')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment