Last active
March 13, 2020 23:50
-
-
Save ksylvan/3dd8e3d8e594a478e2709addecfcecbc to your computer and use it in GitHub Desktop.
Simple 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
# You are building an educational website and want | |
# to create a simple calculator for students to use. | |
# The calculator will only allow addition and subtraction | |
# of non-negative integers. | |
# We also want to allow parentheses in our input. | |
# Given an expression string using | |
# the "+", "-", "(", and ")" | |
# operators like "5+(16-2)", write a function to parse the | |
# string and evaluate the result. | |
import re | |
class Calculate: | |
__NUMS = re.compile(r"\d+") | |
@classmethod | |
def tokens(cls, s): | |
pos = 0 | |
n = len(s) | |
while pos < n: | |
m = Calculate.__NUMS.match(s, pos) | |
if m: | |
yield int(m.group(0)) | |
pos += len(m.group(0)) | |
if pos < n: | |
yield s[pos] | |
pos += 1 | |
def __init__(self, s): | |
self.__tokens = [] | |
for t in Calculate.tokens(s): | |
self.__tokens.append(t) | |
class Context: | |
def __init__(self): | |
self.__stack = [] | |
self.__op = None | |
def __str__(self): | |
return f"Context({self.stack}){'OP: ' + str(self.op) if self.op else ''}" | |
@property | |
def op(self): | |
return self.__op | |
@op.setter | |
def op(self, v): | |
self.__op = v | |
@property | |
def stack(self): | |
return self.__stack | |
def nonEmpty(self): | |
return len(self.stack) != 0 | |
@property | |
def val(self): | |
if self.nonEmpty(): | |
return self.stack[-1] | |
return None | |
def __deal_with_val_in_context(self, cx, v): | |
if cx.op: | |
left = cx.stack.pop() | |
cx.stack.append(cx.op(left, v)) | |
cx.op = None | |
else: | |
cx.stack.append(v) | |
def value(self): | |
l = [] | |
cx = Calculate.Context() | |
for t in self.__tokens: | |
if type(t) is int: | |
self.__deal_with_val_in_context(cx, t) | |
continue | |
if t == '(': | |
l.append(cx) | |
cx = Calculate.Context() | |
continue | |
if t == ')': | |
my_v = cx.val | |
cx = l.pop() | |
if my_v is not None: | |
self.__deal_with_val_in_context(cx, my_v) | |
continue | |
if t == '+': | |
cx.op = lambda a, b: a + b | |
continue | |
if t == '-': | |
cx.op = lambda a, b: a -b | |
return cx.val | |
if __name__ == '__main__': | |
tests = [ | |
("6+9-12", 3), | |
("1+2-3+4-5+6-7", -2), | |
("100+200+300", 600), | |
("1-2-3-0", -4), | |
("255", 255), | |
("5+16-((9-6)-(4-2))+1", 21), | |
("22+(2-4)", 20), | |
("6+9-12", 3), | |
("((1024))", 1024), | |
("1+(2+3)-(4-5)+6", 13), | |
("255", 255)] | |
for t in tests: | |
expr = t[0] | |
correct = t[1] | |
v = Calculate(expr).value() | |
res = "OK" if v == correct else "FAIL" | |
print(f"{res} Calculate({expr}) = {v} [expected {correct}]") | |
# OK Calculate(6+9+12) = 3 [expected 3] | |
# OK Calculate(1+2-3+4-5+6-7) = -2 [expected -2] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment