-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathb_simplified.py
More file actions
139 lines (108 loc) · 5.74 KB
/
Copy pathb_simplified.py
File metadata and controls
139 lines (108 loc) · 5.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# https://contest.yandex.ru/contest/22781/run-report/140478282/
#
# -- Принцип работы --
#
# Калькулятор вычисляет значения арифметических выражений, записанных в обратной польской нотации.
# Для временного сохранения чисел, которые содержатся в арифметическом выражении, используется стек,
# изначально пустой. Строка символов, переданная в калькулятор, разбивается на токены, отделенные
# друг от друга пробелами. Далее для каждого токена выполняется одна из следующих команд:
#
# * Если было получено число, то оно помещается на вершину стека.
# * Если был получен знак арифметической операции, то из стека извлекаются два последних числа и
# вычисляется результат операции, который помещается обратно в стек.
#
# После того как все токены были обработаны, результат арифметического выражения расположен на вершине
# стека.
#
# -- Доказательство корректности --
#
# Если арифметическое выражение не содержит лишних чисел, то результатом будет единственный элемент,
# оставшийся в стеке после обработки всех токенов. В следующих случаях программа завершит работу с
# исключением `ValueError`:
#
# * Исходная строка содержит токен, который не является ни операцией, ни числом.
# * В строке недостаточно чисел для выполнения всех арифметических операций.
#
# -- Временная сложность --
#
# Поскольку обработка каждого токена выполняется за фиксированное число операций, то вычислительная
# сложность определения значения арифметического выражения составляет `O(n)`, где `n` — количество
# токенов в исходной строке.
#
# -- Пространственная сложность --
#
# В худшем возможном варианте все операнды расположены в начале арифметического выражения, а за ними
# следуют операции. В этом случае необходимо будет поместить в стек сразу все числа из выражения.
# Тогда пространственная сложность составит `O(n)`.
from __future__ import annotations
import operator
import re
from collections.abc import Mapping, Iterable, Callable
class CalculatorStack:
values: list[int]
def __init__(self) -> None:
self.values = []
def push(self, value: int) -> None:
self.values.append(value)
def pop(self) -> int:
if not self.values:
raise ValueError('Stack is empty')
return self.values.pop()
def get_result(self) -> int:
if not self.values:
raise ValueError('Stack is empty')
return self.values[-1]
type CalculatorCommandParser = Callable[[re.Match[str]], None]
type OperationCommand = Callable[[int, int], int]
class Calculator:
stack: CalculatorStack
command_parsers: Iterable[tuple[re.Pattern[str], CalculatorCommandParser]]
operations: Mapping[str, OperationCommand] = {
'+': operator.add,
'-': operator.sub,
'*': operator.mul,
'/': operator.floordiv,
}
def __init__(self) -> None:
self.stack = CalculatorStack()
self.command_parsers = self.get_command_parsers()
def get_command_parsers(self) -> Iterable[tuple[re.Pattern[str], CalculatorCommandParser]]:
return [(
re.compile(token_pattern),
command_parser,
) for token_pattern, command_parser in [
(r'-?\d+', self._parse_value),
(r'[+\-*/]', self._parse_operation),
]]
def calculate(self, tokens_str: str) -> int:
self.stack = CalculatorStack()
for token in tokens_str.split():
self._parse_token(token)
return self.stack.get_result()
def _parse_token(self, token: str) -> None:
for token_re, command_parser in self.command_parsers:
token_match = token_re.fullmatch(token)
if token_match is None:
continue
command_parser(token_match)
return
raise ValueError('Invalid token')
def _parse_value(self, token_match: re.Match[str]) -> None:
value = int(token_match.group())
self.stack.push(value)
def _parse_operation(self, token_match: re.Match[str]) -> None:
operation_char = token_match.group()
operation = self.operations.get(operation_char)
if operation is None:
raise ValueError('Invalid operation symbol')
b = self.stack.pop()
a = self.stack.pop()
result = operation(a, b)
self.stack.push(result)
def main() -> None:
tokens_str = input().strip()
calculator = Calculator()
result = calculator.calculate(tokens_str)
print(result)
if __name__ == '__main__':
main()