Занимательная программительниншна.
Калькулятор - нет ничего тупее...
Пост не рекомендуется для лиц с тонкой душевной организацией и для тех, кто совершенен в своем деле...
.
.
...и вообще не читайте его.
Понятненько?
.
.
.
.
а теперь?
Тут глупости, написанные таким же глупым человеком. Поэтому считайте, что пост написал умственно отсталый инвалид, который заслуживает снисхождения.
Каждый, думающий, что он умеет в программирование, должен нацарапать калькулятор и безумно гордиться своей поделкой(С) выпячив грудь, ибо его шэдэвор неповторим, уникален (нет) и вапще я у мамы самый красивый.
Но думать что ты Гуру кодинга вовосе необязательно, достаточно верить в себя и быть настолько смелым, чтобы показать свои высеры публике, подставив панамку.
Ну и главное не забывать делать вид, что ты во всём этом разбираешься, и сделать содержимое ниже - это как два пальца об асфальт, оставив за кадром сопли, слёзы и прочие выделения человеческого организма.
Ну штош, пришло время приступить и надеяться на то, что всё было предусмотрено, любая ситуация была смоделирована, тесты прошли успешно и, казалось бы, можно уже начать заниматься математикой.
Самое главное при создании какой-либо программулины - это диалог с тем, кто пользуется вашими поделками.
Но, как вы понимаете, это вовсе не обязательно, поэтому в первой версии вычислительного скрипта поздороваемся сдержанно и без всяких там панибратств.
Поприветствуем юзверя)))
# Ты запустил эту хуйню. Теперь обратного пути нет.
print("Простой калькулятор. Введи q, чтобы выйти.") # Тебе дан шанс пойти по своим делам или воспользоваться каклькуляторов в тефоне
# ---------------- Погнали в бесконечный цикл!!! Или тебе надо два плют два сложить?? ----------------
while True: # Игра началась. Хули смотришь? Действуй.Про вайл тру - после кода ниж)))
# Пытаемся вытянуть из пользователя первое число. Стрип на случай, если он долбоёб и понаставил пробелов.
first = input("Первое число (или q для выхода): ").strip()
if first.lower() == "q": # Умный выбор. Всё бросить и уйти. Завидую.
print("Пока!") # Прощай, свободный человек!
break # Вырываемся из ада бесконечного цикла. УРА!
# ---------------- ловим чуш на вводе ----------------
try:
a = float(first) # Попытка превратить введённый бред в число. Молимся.
except ValueError: # А вот и нет, тут не число! Типуля за капмуктером опять что-то наклацал.
print("Это не число. Попробуй ещё раз.") # Терпеливо объясняем очевидное. Опять.А можно было бы и матом
continue # Отматываем плёнку и заставляем его страдать с начала.
# ---------------- че делаем-то? ----------------
op = input("Операция (+ - * /): ").strip() # Выбор, от которого зависит всё.
# Проверяем, не долбоёб ли пользователь, который ввёл знак из другого математиичкского мирв.
if op != "+" and op != "-" and op != "*" and op != "/":
print("Нужна одна из операций: + - * /") # Говорим по-русски, что мы тут умеем.
continue # Назад, в начало, учить матешу!
# ---------------- а теперь фторой кондедат ----------------
second = input("Второе число: ").strip() # Снова пытаемся добыть цифру из дикого зверя по имени Пользователь.
try:
b = float(second) # Вторая попытка. Пальцы скрещены.
except ValueError: # Блядь! Опять провал. Он неисправим.
print("Это не число. Попробуй ещё раз.") #вздох Повторяем мантру.
continue
# ----------------смотрим че получилось ----------------
if op == "+": # Если выбрал плюс — всё просто, как два пальца обоссать
result = a + b # Складываем. Надеемся, числа не слишком большие.
elif op == "-": # Вычитание. Уносим готовенькое.
result = a - b
elif op == "*": # Умножение. Всё растёт, как на дрожжах.
result = a * b
else: # А вот и деление... Наше всё, источник вечных ошибок.
if b == 0: # ОПАСНОСТЬ! Пользователь пытается разделить на ноль и схлопнуть вселенную кхуям
print("Ошибка: деление на ноль") # Срочно останавливаем это безумие.
continue # И отправляем его в начало без результата. Пусть думает над своим преступлением.
result = a / b # Если ноля нет — делим спокойно. Фух.
# ---------------- ФИНАЛ. ПОКАЗЫВАЕМ, НА ЧТО СПОСОБНА ЭТА ХУЙНЯ ----------------
print("Результат:", result) # Выводим то, ради чего всё это начиналось. Наслаждайся, мамкин математик.
Как видите это вообще такое простое скучное и беспонтовое решение, и на первый взгляд самодостаточное.
но!!!!
Это фигня собачачья!!! Потому что делает пользование калькулятором невохможным, ибо взаимодействие с ним выглядит так:
Первое число (или q для выхода): 12
Операция (+ - * /): +
Второе число: 85
Результат: 97.0
Первое число (или q для выхода): 23
Операция (+ - * /): *
Второе число: 45879
Результат: 1055217.0
Первое число (или q для выхода):
После ввода каждого числа нада жмать Интер, а это тупо, садомазохично и не юзерфрендли. Юзер не секретарша на клавиатуре. Он царь и бог, который хочет ввести 5 + 3 и сразу, блять, получить ответ, а не участвовать в этом концерте одинокого пианиста для трёх рук - число-энтер-оператор-энтер-число-энтер.
Логичным будет задать вопрос: "асхуяле?" Потому что консольный ввод (input()) в питоне это тупой, линейный идиот. Он читает строку только до переноса строки. Он не умеет в реальном времени, как человек в калькуляторе. Он слепой старик, который просит тебя говорить по одному слову и стучать палкой по голове после каждого.
Можно отказаться от input() и читать всё одной строкой, как нормальные люди. Потом самому разбирать, где числа, где операторы.
Это круто, мощно, молодёжно, но это уже не 10 строк кода, а отдельная ёбля.
# Примерно так выглядит вход в лигу крутых типочков
expression = input("Введи весь пример (например, 5+3): ").replace(" ", "")
# А дальше надо эту строку распетушить на числа и знаки внимания
# проверить, что там нет букв, обработать приоритеты (приоритеты - это
# што вычисляется вначаеле - сложение или умножение, в скопках или за ними
# думаю со школы помните)
# Короче, адский труд для ТСа. Но зато одной строкой!
Есть два стула
Есть несколько путей:
Путь Воина (сделать свою дичь) - сложна...
Путь хитросделанного и рационального (найти готовую библиотеку) - просто
Ну или воспользоваться тем, который есть в вашей, простигосподи, винде - но тогда и пост нахуй не нужен.
Путь воена, Света:
Итак, блять, путь воина. Это не для слабаков. Это когда ты говоришь стандартному input(): иди нахуй со своим энтером и начинаешь парсить (левои или правой - не важно) всё сам, разрывая строку голыми зубами.
Зырим
Отказываемся от пошагового опроса. Берем всё выражение одной строкой: "5 + 3 * 2". А потом начинается адский труд (для ТС конечно же, ибо тру программистам - это как в носе ковырять): надо превратить эту строку в числа и операторы, понять приоритет, посчитать. Это как узнать рецепт кокаколы с помощью масспектрометра и наколотить такую же с газиками. Ну почти.
Первым делом надо выкинуть из строки пробелы, чтобы они не мешались. Пользователь мог написать и 5+3, и 5 + 3, и 5 + 3 - а нам похуй.
expression = input("Введи выражение, герой (типа 5+3 или 2*4): ").replace(" ", "")
# теперь тут голая строка без пробелов "5+3*2"
Теперь разбираем на хапчасти эту строку (числа и операторы), это самый ебучий момент. Надо пройтись по строке символ за символом и понять, где кончается одно число и начинается оператор. Число может быть 5, а может быть -12.5. Оператор, если что это +, -, *, /. Может быть даже ** для степени.
Делается это обычно через конченный автомат (state machine). Грубо говоря идём по строке и включив размышлительниншну бурчим себе под нос:
Сейчас я читаю цифру - это часть числа. Встретил точку - это тоже часть числа (дробная). Встретил плюс/минус/умножить/поделить - всё, число закончилось. Запоминаем число, запоминаем оператор, начинаем собирать новое число.
вот простой пример:
primer = [] # Сюда будем складывать то, шо нашли
current_number = '' # Здесь накапливаем цифры текущего числа
for char in expression:
if char.isdigit() or char == '.': # Если это цифра или точка то это часть числа
current_number += char
else: # Если это не цифра (значит, имератор)
if current_number: # Если до этого собирали число
primer.append(float(current_number)) # записать число
current_number = '' # обнулить сборшык
primer.append(char) # записать сам оператор
# не забыть про последнее число в строке
if current_number:
primer.append(float(current_number))
# Теперь вся хуйня для "5+3*2" будет выглядеть так: [5.0, '+', 3.0, '*', 2.0]
Помните про уроки матепатики и приоритеты считания?
Вот тут пиздец. Есть список, который primer - это [5.0, '+', 3.0, '*', 2.0]. Если выполнять слева направо, получится (5+3)*2 = 16. Но математика говорит, что умножение приоритетнее!!!!111 Должно быть 5 + (3*2) = 11.
Значит, нужно пройти по списку несколько раз. Сначала найти все умножения и деления (*, /), выполнить их, и заменить в списке тройку элементов (число, оператор, число) на одно число (результат). Потом повторить для сложения и вычитания (+, -).
Это можно делать на месте, изменяя список primer, пока в нём не останется одно число которое итоговый результат.
например
i = 0
while i < len(primer):
if primer[i] == '*':
# умножение детектед. ьерём предыдущее и следующее числа
result = primer[i-1] * primer[i+1]
# меняем тройку элементов на результат: [..., a, '*', b, ...] -> [..., result, ...]
primer[i-1:i+2] = [result] # заменить нада
i -= 1 # откатитть индекс, потому что список стал короче
i += 1
# и анал огично для '/', затем для '+', '-'
Обрабатываем нештатное ситуфциё - без этого никуда. Юзер непредсказуем! А что, если он ввёл 5++3 или 12.*7 или просто "у меня большой хуй"? Парсер должен не сломаться, а красиво послать нахуй. Нужны проверки на каждом шагу, например в строке только разрешённые символы (цифры, точка, операторы), число точек в числе не больше одной, после оператора идёт число (а не другой оператор), деления на ноль нет, список primer в итоге имеет правильную структуру - чередование число-оператор-число или 2 + хуй?
в итоге получается полный (нет) контроль. Можно добавить скобки (), унарные минусы -5, степени **, функции sin(). Но цена этого огромная, ебучая работа, тонны кода для разбора и валидации, и постоянные баги на ёбнутых случаях вроде -.5 + .3).
Это путь для тех, кому надо понять, как всё устроено изнутри, или для тех, кому нужна кастомная, очень специфичная штука некакувсех. Для обычного калькулятора это избыточное оверкилл-масло, высеченное из гранита ногтяме Кукухи.
Но, резюмируя, это не для пользы, а это про силу воли, упрямство и желание сделать всё своими руками, даже если для этого придётся изобрести свою велик срулём. Удачи че, воен. Ты её, блять, заслужил.
Собствено вот, зырьте какая большая ебала получается:
# арём главной функцией, дескать она тут правит бал!!!111
def calculate_warrior_style():
"""
Калькулятор ниибического вида.
Принимает примерчики типа "5+3*2" и считает с учётом приоритета вычислкний.
Поддерживает скобки, минусы и степень (**).
"""
# вводим в курс дела, юзер должен понять, куда он попал.
print("=== КАЛЬКУЛЯТОР ===")
print("пеши выражение одной строкой. Поддерживаются + - * / **, скобки и числа с точкой.")
print("Пример: 5+3*2 или 10.5 / 2 - 1 или -(2+3)**2") #юзер всё равно чтото сломает....
print("Введи 'q' для выхода. Удачи, ёпта.\n")
# Бесконечный цикл. Выход только через break или смерть.
while True:
# спрашиваем "че надо?".
raw_input = input("Твоё выражение (или q): ").strip() # strip() отрезает пробелы по краям, на случай если он долбоёб.
if raw_input.lower() == 'q': # Если ввёл q (даже заглавную) это капитуляция.
print("пока, слабак") # Прощаемся с честью.
break # Ломаем цикл. Свобода. теперь питание компьютера можно отключить
# наведем порядок, уберем хлам
expression = raw_input.replace(" ", "") # выкуриваем ВСЕ пробелы из середины. Они нам не нужны.
if not expression: # Если после чистки осталась пустота...
print("Ты ввёл пустоту. Великий воин молчит.") # ...сообщаем о творческом кризисе.
continue # Отматываем цикл назад, пусть вводит заново, ёбаный хуй
# превращаем строку в запчасти).
try:
primer = primerize(expression) # Вызываем функцию-ошибалку. Она может выплюнуть ошибку.
except ValueError as exc: # Если мясорубка сломалась и выплюнула ValueError...
print(f"Ошибка: {exc}") # ...показываем пользователю, в чём он мудак.
continue # И заставляем его страдать с начала.
# гля прикол - делаем из (5+3) в обратную польскую запись (5 3 +). Так легче считать.лол
try:
rpn = to_rpn(primer) # косплей сортировочной станции. Мозголомно, но работает.
except ValueError as exc: # Если алгоритм нашел кривые скобки...
print(f"Ошибка: {exc}") # ...тыкаем пользователя в его же косяк.
continue # Нахуй иди исправляй.
# смотрим что наваяла обратная польская запись.
try:
result = eval_rpn(rpn) # Берём и начинаем жать.
except ValueError as exc: # Если не хватило чисел или деление на ноль...
print(f"Ошибка: {exc}") # ...объясняем, что он натворил.
continue # И отправляем в начало.
# неужели написал правильно??? ахуеть - выдаем результат.
print(f"Результат твоего высера: {result}") # Пользователь доволен. Мы измотаны.
print("-" * 40) # Рисуем разделитель. Эстетика, блять.
# ========== функция primerize о которой была речь выше) ==========
# Берёт строку и с остервенением кромсает её на числа, операторы и скобки.
def primerize(expression):
primer = [] # Сюда будем складывать добычу: числа и операторы.
i = 0 # Указатель. Бежит по строке, как алкаш по бутылкам.
prev_type = None # Следим, что было прошлым: число('num'), оператор('op'), скобка. Чтобы отличить унарный минус.
# Главный цикл чтения строки. Пока не кончится.
while i < len(expression):
char = expression[i] # Берём текущий символ.
# Если пробел то игнорируем, их уже вырезали, но на всякий.
if char.isspace():
i += 1
continue
# Если символ цифра или точка, значит, это начало чилса
if char.isdigit() or char == ".":
start = i # Запоминаем, где число началось.
dots = 0 # Щёчик точек в числе (больше одной это ошибка).
digits = 0 # Счётчик цифр (ноль цифр это тоже ошибка).
# Пока не кончилась строка и идут цифры/точки, собираем число.
while i < len(expression) and (expression[i].isdigit() or expression[i] == "."):
if expression[i] == ".":
dots += 1 # Нашли точку и запомнели
else:
digits += 1 # Нашли цифру. Тоже запоминаем.
i += 1 # Переходим к следующему символу.
# Вырезаем кусок строки от start до i это наше число в виде строки.
number_str = expression[start:i]
# Проверяем фсю хуйню: если точек >1 или цифр нет, то это не число.
if dots > 1 or digits == 0:
raise ValueError(f"это не число блять!!!! '{number_str}'") # Выбрасываем ошибку наверх.
primer.append(float(number_str)) # Превращаем строку в float и кладём в список.
prev_type = "num" # Запоминаем, что последним было число.
continue # Переходим к следующему символу.
# Если символ звёздочка и следующая за ней тоже звёздочка, это оператор степени '**'.
if char == "*" and i + 1 < len(expression) and expression[i + 1] == "*":
op = "**" # Это оператор.
i += 2 # Перепрыгиваем сразу два символа.
# Иначе, если символ,то один из разрешённых операторов или скобок...
elif char in "+-*/()":
op = char # Это наш оператор или скобка.
i += 1 # Переходим к следующему символу.
else:
# Если символ не опознан это пиздец. Выбрасываем ошибку.
raise ValueError(f"че за хуйня? '{char}'")
# ----- Обработка ЛЕВОЙ скобки ( -----
if op == "(":
# в нашем случае цобка не может идти после числа или другой правой скобки без оператора. Пример: 5( или )(.
if prev_type in ("num", ")"):
raise ValueError("пропущен оператор перед '('")
primer.append(op) # Кладём скобку в список.
prev_type = "(" # Запоминаем, что была скобка.
continue # Едем дальше.
# ----- Обработка ПРАВОЙ скобки ')' -----
if op == ")":
# Скобка не может идти после оператора или другой левой скобки. Пример: +) или ().
if prev_type in (None, "op", "("):
raise ValueError("лишняя ')' или пустые скобки, повнимательнее там")
primer.append(op) # Кладём скобку в список.
prev_type = ")" # Запоминаем.
continue # Едем дальше.
# ----- Обработка УНАРНОГО МИНУСА (например, -5 или -(2+3)) -----
# Если текущий оператор '-' и перед ним ничего не было (начало строки) или был оператор/левая скобка...
if op == "-" and (prev_type is None or prev_type in ("op", "(")):
primer.append("u-") # Кладём специальный "унарный минус".
prev_type = "op" # Запоминаем, что был оператор.
continue # Едем дальше.
# ----- Обработка ОБЫЧНЫХ БИНАРНЫХ ОПЕРАТОРОВ (+, -, *, /, **) -----
# Оператор не может быть в начале или после другого оператора/левой скобки. Пример: +5 или (*.
if prev_type in (None, "op", "("):
raise ValueError(f"оператор '{op}' стоит не на месте")
primer.append(op) # Всё зоебок, кладём оператор в список.
prev_type = "op" # Запоминаем.
# Проверка после разбора всей строки. Выражение не может кончиться оператором или открытой скобкой.
if prev_type in (None, "op", "("):
raise ValueError("так примеры не пишут! выражение не может заканчиваться оператором или '('")
return primer # Возвращаем гордый список токенов.
# ========== ФУНКЦИЯ-ПЕРЕВОДЧИК (to_rpn) ==========
# Превращает обычную запись в обратную польскую с помощью сортировочной станции.
def to_rpn(primer):
output = [] # Сюда пойдёт итоговая RPN-последовательность.
stack = [] # Временный стек для операторов и скобок.
# Словарь приоритетов: чем больше цифра, тем круче оператор.
precedence = {"u-": 3, "**": 4, "*": 2, "/": 2, "+": 1, "-": 1}
right_assoc = {"u-", "**"} # Множество право-ассоциативных операторов (для степени и унарного минуса).
# Идём по каждому primer на входе.
for token in primer:
# Если оно число, сразу отправляем его на выход.
if isinstance(token, float):
output.append(token)
continue
# Если оно левая скобка, пихаем её в стек.
if token == "(":
stack.append(token)
continue
# Если правая скобка, выталкиваем из стека ВСЁ до левой скобки в output.
if token == ")":
while stack and stack[-1] != "(": # Пока стек не пуст и наверху не '('...
output.append(stack.pop()) # ...выталкиваем операторы в output.
if not stack: # Если стек опустел, а '(' так и не нашли...
raise ValueError("скобки не совпадают") # ...скобки кривые.
stack.pop() # Выкидываем саму '(' из стека.
continue
# Если это оператор (унарный или бинарный)...
if token in precedence:
# Пока в стеке есть операторы с БОЛЬШИМ ИЛИ РАВНЫМ (для лево-ассоциативных) приоритетом...
while stack and stack[-1] in precedence:
top = stack[-1] # Смотрим на верхний оператор в стеке.
# Сравниваем приоритеты с учётом ассоциативности.
if (token not in right_assoc and precedence[token] <= precedence[top]) or (
token in right_assoc and precedence[token] < precedence[top]
):
output.append(stack.pop()) # Выталкиваем старый оператор в output.
else:
break # Иначе останавливаемся.
stack.append(token) # Кладём текущий оператор в стек.
continue
# Сюда попасть не должны. На всякий — ошибка.
raise ValueError(f"непонятно че вапще'{token}'")
# Когда все эти штуки обработаны, выталкиваем оставшиеся в стеке операторы в output.
while stack:
top = stack.pop() # Берём верхний элемент.
if top in ("(", ")"): # Если это скобка значит, они не совпали.
raise ValueError("скобки не совпадают")
output.append(top) # Иначе отправляем в output.
return output # Возвращаем чистую, прекрасную рпн.
# ========== ФУНКЦИЯ-ВЫЧИСЛИТЕЛЬ (eval_rpn) ==========
# Считает результат по обратной польской записи.
def eval_rpn(primer):
stack = [] # Рабочий стек для чисел.
# Идём по каждому токену в RPN-последовательности.
for token in primer:
# Если токен число, просто пихаем его в стек.
if isinstance(token, float):
stack.append(token)
continue
# Если токен унарный минус, берём одно число из стека, меняем знак и кладём обратно.
if token == "u-":
if len(stack) < 1: # Проверяем, что в стеке есть хотя бы одно число.
raise ValueError("унарный минус без числа")
stack.append(-stack.pop()) # Берём число, меняем знак, кладём обратно.
continue
# Для бинарных операторов нужно ДВА числа из стека.
if len(stack) < 2:
raise ValueError("недостаточно чисел для операции")
b = stack.pop() # Второй аргумент (справа).
a = stack.pop() # Первый аргумент (слева).
# Смотрим, какой оператор, и делаем соответствующую хуйню.
if token == "+":
stack.append(a + b)
elif token == "-":
stack.append(a - b)
elif token == "*":
stack.append(a * b)
elif token == "/":
if b == 0: # Проверяем святое деление на ноль.
raise ValueError("еблан - деление на ноль")
stack.append(a / b)
elif token == "**":
stack.append(a ** b)
else:
raise ValueError(f"че надо-то? '{token}'")
# В конце в стеке должно остаться РОВНО ОДНО число результат.
if len(stack) != 1:
raise ValueError("ошибка вычисления")
return stack[0] # Возвращаем это число плод наших трудов.
# ========== ТОЧКА ВХОДА ==========
if __name__ == "__main__":
calculate_warrior_style() # ...запуск шайтан машыны
Оно трудное, большое и никому нахуй неужное, но существует))) и с дружелюбным "интерфейсом"
=== КАЛЬКУЛЯТОР ===
пеши выражение одной строкой. Поддерживаются + - * / **, скобки и числа с точкой.
Пример: 5+3*2 или 10.5 / 2 - 1 или -(2+3)**2
Введи 'q' для выхода. Удачи, ёпта.
Твоё выражение (или q): (2+9*458)-148*789/36987
Результат твоего высера: 4120.842890745397
----------------------------------------
ну и на больших степенях не охуевайте - эта хуёва туча, умноженная сама на себя 4545 рази в результате это число с десятками тысяч цифр. Вот нахуя оно вам? Оно настолько огромное, что стандартный тип float (число с плавающей запятой) в питоне просто не может его в себя вместить. У float есть максимальный предел, после которого он говорит "Идите нахуй Извини, ботан, я сдаюсь, это бесконечность и мы такого еще не проходили"
Твоё выражение (или q): 5 + 7 + (4895875/785545+987565 )**789 + 78798**4545
Traceback (most recent call last):
File "/calc.py", line 248, in <module>
calculate_warrior_style() # ...запуск шайтан машыны
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/calc.py", line 44, in calculate_warrior_style
result = eval_rpn(rpn) # Берём и начинаем жать.
^^^^^^^^^^^^^
File "/calc.py", line 235, in eval_rpn
stack.append(a ** b)
~~^^~~
OverflowError: (34, 'Numerical result out of range')
Так что вот вам ещё доза ненужной информации на сегодня, ну для разнообразия, думаю, пойдёт. Ах да, и наверняка в комментарии придут специалисты в данной области которые скажут что в ТС мудак - собственно ради этого всё и писалось 🥹
Давно обратил внимание, что тюркская народная музыка заебись ложится на танцевальный бит. Вот, прям, на кислоту вот эту рейвовую. Но, увы, мне это не очень заходит. Объективно понимаю, что норм тема,...
В общем не случился у нас тургояк в этом году, рискованно с погодой (а ну как реально бы +10 было и че делать? В домике сидеть?) и как то очень дорого для ночи в домике без питания…
БДСМ пляжи?