Created
November 15, 2020 10:49
-
-
Save myuanz/d7f43062d3ad2fe746469c27026147d6 to your computer and use it in GitHub Desktop.
简单的 Python 带括号四则运算计算器 / Simple Python elementary arithmetic calculator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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