Отлично! Ты попросил сделать калькулятор для Pydroid3. Так как цель — понять материал, ниже я объясню по шагам, что именно делает калькулятор, и дам готовый код, который можно просто вставить в Pydroid3 и запустить.
Что умеет этот калькулятор
- Вычисляет арифметические выражения с операциями: сложение +, вычитание -, умножение *, деление / и возведение в степень ^.
- Поддерживает скобки: ( … ).
- Поддерживает десятичные числа.
- Поддерживает унарный минус (например, -5, (-3 + 2)).
- Безопасный альтернативный подход к eval: расчет идет через разбор выражения и постфиксную запись (обратную польскую нотацию), чтобы избежать рискованных вызовов eval.
Как работает калькулятор (пошагово, для понимания)
- Препроcсинг выражения
- Убираем пробелы и преобразуем несколько форм возведения степени в единый символ ^ (например, в выражении ** превращаемось в ^).
- Разбор на токены
- Разделяем строку на числа (например 12, 3.5), операторы (+, -, *, /, ^) и скобки.
- Преобразование в постфиксную запись (обратная польская нотация)
- Используется алгоритм Шантинга-Ярда.
- Сразу поддерживаем унарный минус: если минус стоит на месте, где ожидается число (в начале выражения, после скобки или после другого оператора), он трактуется как унарный минус.
- В постфиксной записи число идёт перед операцией, например: "3 4 +".
- Вычисление постфиксной записи
- С помощью стека вычисляем результат по постфиксной записи.
- Поддерживаются все нужные операции, включая унарный минус.
- Результат
- Выводится значение выражения. Если выражение неверное, выводится понятная ошибка.
Инструкция по запуску в Pydroid3
- Открой новое пустое Python-файло в Pydroid3.
- Скопируй сюда код ниже.
- Выполни файл. В консоли можно вводить выражения, например: 2*(3+4), -5+2, (1+2)^3.
- Чтобы выйти, введи exit или quit.
Готовый код (консольный калькулятор на Python)
- Скопируй весь код ниже и сохрани как, например, calc.py, затем запусти в Pydroid3.
# Консольный калькулятор для Pydroid3
# Поддерживает: +, -, *, /, ^, скобки, десятичные числа, унарный минус
def preprocess(expr: str) -> str:
# Убираем пробелы и преобразуем двойной звездочка в ^
s = expr.replace(' ', '')
s = s.replace('**', '^')
return s
def is_number(token: str) -> bool:
try:
float(token)
return True
except ValueError:
return False
def tokenize(s: str):
tokens = []
i = 0
while i < len(s):
c = s[i]
if c.isdigit() or c == '.':
j = i
dot = 0
while j < len(s) and (s[j].isdigit() or s[j] == '.'):
if s[j] == '.':
dot += 1
if dot > 1:
break
j += 1
tokens.append(s[i:j])
i = j
elif c in '+-*/^()':
tokens.append(c)
i += 1
else:
raise ValueError(f"Недопустимый символ: '{c}'")
return tokens
def precedence(op: str) -> int:
if op == 'u-': # унарный минус
return 4
if op == '^':
return 3
if op in ('*', '/'):
return 2
if op in ('+', '-'):
return 1
return 0
def associativity(op: str) -> str:
# Сложение и умножение — левоассоциативны; ^ и унарный минус — правоассоциативны
if op in ('^', 'u-'):
return 'R'
return 'L'
def to_rpn(tokens):
out = []
stack = []
# prev держит тип предыдущего токена: 'NUM', 'OP', 'LP', 'RP', 'START'
prev = 'START'
for t in tokens:
if is_number(t):
out.append(t)
prev = 'NUM'
elif t in '+-*/^':
# Определяем унарный минус: если '-' стоит на позиции, где ожидался операнд
if t == '-' and (prev in ('START', 'OP', 'LP')):
t = 'u-'
# Выносим из стека все операторы с большими/равными приоритетами
while stack and stack[-1] != '(' and (
(precedence(t) < precedence(stack[-1])) or
(precedence(t) == precedence(stack[-1]) and associativity(t) == 'L')
):
out.append(stack.pop())
stack.append(t)
prev = 'OP'
elif t == '(':
stack.append(t)
prev = 'LP'
elif t == ')':
# Выталкиваем всё до '('
while stack and stack[-1] != '(':
out.append(stack.pop())
if not stack:
raise ValueError("Скобки не сбалансированы")
stack.pop() # убрать '('
prev = 'RP'
else:
raise ValueError(f"Неизвестный токен: {t}")
# Очищаем стек
while stack:
if stack[-1] in '()':
raise ValueError("Скобки не сбалансированы")
out.append(stack.pop())
return out
def evaluate_rpn(rpn):
stack = []
for tok in rpn:
if is_number(tok):
stack.append(float(tok))
elif tok == 'u-':
if not stack:
raise ValueError("Недостаточно операндов для унарного минуса")
a = stack.pop()
stack.append(-a)
else:
if len(stack) < 2:
raise ValueError("Недостаточно операндов для операции")
b = stack.pop()
a = stack.pop()
if tok == '+':
stack.append(a + b)
elif tok == '-':
stack.append(a - b)
elif tok == '*':
stack.append(a * b)
elif tok == '/':
stack.append(a / b)
elif tok == '^':
stack.append(a ** b)
else:
raise ValueError(f"Неизвестная операция: {tok}")
if len(stack) != 1:
raise ValueError("Неправильное выражение")
return stack[0]
def calculate(expr: str) -> float:
if not expr:
raise ValueError("Пустое выражение")
s = preprocess(expr)
tokens = tokenize(s)
rpn = to_rpn(tokens)
result = evaluate_rpn(rpn)
return result
def main():
print("Калькулятор для Pydroid3. Введите выражение (пример: 2*(3+4) или -5+2).")
print("Чтобы выйти, введите exit.")
while True:
s = input("Expr> ").strip()
if s.lower() in ('exit', 'quit', 'q'):
break
try:
res = calculate(s)
# Округлим для удобства вывода
print("=", res)
except Exception as e:
print("Ошибка:", e)
if __name__ == '__main__':
main()
Дополнительный вариант: простой графический калькулятор (Tkinter) Если хочешь графический интерфейс (кнопки и дисплей), можно сделать минимальный Tkinter-приложение. Это потребует установленного модуля tkinter в твоем окружении Python. В Pydroid3 он часто доступен. Сообщи, если хочешь такой вариант — дам упрощенный код на Tkinter и объяснение, как его запускать.
Пояснения по базовым концептам (для закрепления)
- Токены: маленькие части выражения — числа и знаки операций.
- Постфиксная запись (обратная польская запись): порядок записи, при котором операция идёт после своих операндов, например 3 4 + вместо 3 + 4. Такой подход упрощает вычисления.
- Унарный минус: минус перед числом без явного числа слева, например −5. В выражении он трактуется как отдельная операция «уар-минус» и вычисляется как отрицательное число.
- Ассоциативность и приоритеты: какие операции сначала. Возведение в степень выполняется отдельно и справа (правоассоциация), сложение/вычитание — слева направо.
Если хочешь, могу:
- сделать упрощенную версию без постфикса, но через eval с ограниченным набором разрешённых операций (меньше риска, но менее безопасно без проверок), или
- адаптировать калькулятор под учебные задачи 4 класса (добавить работу с дробями, проценты и простые примеры).
Скажи, какой вариант тебе подходит, и можно дополнительно адаптировать стиль объяснений под конкретный уровень и примеры из твоего учебника.