Как сделать цикличный калькулятор на python
Перейти к содержимому

Как сделать цикличный калькулятор на python

  • автор:

Циклы в программировании. Цикл while

Циклы являются такой же важной частью структурного программирования, как условные операторы. С помощью циклов можно организовать повторение выполнения участков кода. Потребность в этом возникает довольно часто. Например, пользователь последовательно вводит числа, и каждое из них требуется добавлять к общей сумме. Или нужно вывести на экран квадраты ряда натуральных чисел и тому подобные задачи.

Цикл while

«While» переводится с английского как «пока». Но не в смысле «до свидания», а в смысле «пока имеем это, делаем то».

Можно сказать, while является универсальным циклом. Он присутствует во всех языках, поддерживающих структурное программирование, в том числе в Python. Его синтаксис обобщенно для всех языков можно выразить так:

while логическое_выражение

Это похоже на условный оператор if . Однако в случае циклических операторов их тела могут выполняться далеко не один раз. В случае if , если логическое выражение в заголовке возвращает истину, то тело выполняется единожды. После этого поток выполнения программы возвращается в основную ветку и выполняет следующие выражения, расположенные ниже всей конструкции условного оператора.

В случае while , после того как его тело выполнено, поток возвращается к заголовку цикла и снова проверяет условие. Если логическое выражение возвращает истину, то тело снова выполняется. Потом снова возвращаемся к заголовку и так далее.

Цикл завершает свою работу только тогда, когда логическое выражение в заголовке возвращает ложь, то есть условие выполнения цикла больше не соблюдается. После этого поток выполнения перемещается к выражениям, расположенным ниже всего цикла. Говорят, «происходит выход из цикла».

Рассмотрите блок-схему цикла while .

Блок-схема цикла while

На ней ярко-голубыми прямоугольниками обозначена основная ветка программы, ромбом – заголовок цикла с логическим выражением, бирюзовым прямоугольником – тело цикла.

С циклом while возможны две исключительные ситуации:

  • Если при первом заходе в цикл логическое выражение возвращает False , то тело цикла не выполняется ни разу. Эту ситуацию можно считать нормальной, так как при определенных условиях логика программы может предполагать отсутствие необходимости в выполнении выражений тела цикла.
  • Если логическое выражение в заголовке while никогда не возвращает False , а всегда остается равным True , то цикл никогда не завершится, если только в его теле нет оператора принудительного выхода из цикла ( break ) или вызовов функций выхода из программы – quit() , exit() в случае Python. Если цикл повторяется и повторяется бесконечное количество раз, то в программе происходит зацикливание. В это время она зависает и самостоятельно завершиться не может.

Вспомним наш пример из урока про исключения. Пользователь должен ввести целое число. Поскольку функция input() возвращает строку, то программный код должен преобразовать введенное к целочисленному типу с помощью функции int() . Однако, если были введены символы, не являющиеся цифрами, то возникает исключение ValueError , которое обрабатывается веткой except . На этом программа завершается.

Другими словами, если бы программа предполагала дальнейшие действия с числом (например, проверку на четность), а она его не получила, то единственное, что программа могла сделать, это закончить свою работу досрочно.

Но ведь можно просить и просить пользователя корректно вести число, пока он его не введет. Вот как может выглядеть реализующий это код:

n = input("Введите целое число: ") while type(n) != int: try: n = int(n) except ValueError: print("Неправильно ввели!") n = input("Введите целое число: ") if n % 2 == 0: print("Четное") else: print("Нечетное")

Примечание 1. Не забываем, в языке программирования Python в конце заголовков сложных инструкций ставится двоеточие.

Примечание 2. В выражении type(n) != int с помощью функции type() проверяется тип переменной n . Если он не равен int , то есть значение n не является целым числом, а является в данном случае строкой, то выражение возвращает истину. Если же тип n равен int , то данное логическое выражение возвращает ложь.

Примечание 3. Оператор % в языке Python используется для нахождения остатка от деления. Так, если число четное, то оно без остатка делится на 2, то есть остаток будет равен нулю. Если число нечетное, то остаток будет равен единице.

Проследим алгоритм выполнения этого кода. Пользователь вводит данные, они имеют строковый тип и присваиваются переменной n . В заголовке while проверяется тип n . При первом входе в цикл тип n всегда строковый, то есть он не равен int . Следовательно, логическое выражение возвращает истину, что позволяет зайти в тело цикла.

Здесь в ветке try совершается попытка преобразования строки к целочисленному типу. Если она была удачной, то ветка except пропускается, и поток выполнения снова возвращается к заголовку while .

Теперь n связана с целым числом, следовательно, ее тип int , который не может быть не равен int . Он ему равен. Таким образом логическое выражение type(n) != int возвращает False , и весь цикл завершает свою работу. Далее поток выполнения переходит к оператору if-else, находящемуся в основной ветке программы. Здесь могло бы находиться что угодно, не обязательно условный оператор.

Вернемся назад. Если в теле try попытка преобразования к числу была неудачной, и было выброшено исключение ValueError , то поток выполнения программы отправляется в ветку except и выполняет находящиеся здесь выражения, последнее из которых просит пользователя снова ввести данные. Переменная n теперь имеет новое значение.

После завершения except снова проверяется логическое выражение в заголовке цикла. Оно даст True , так как значение n по-прежнему строка.

Выход из цикла возможен только тогда, когда значение n будет успешно конвертировано в число.

Рассмотрим следующий пример:

total = 100 i = 0 while i  5: n = int(input()) total = total - n i = i + 1 print("Осталось", total)

Сколько раз «прокрутится» цикл в этой программе, то есть сколько итераций он сделает? Ответ: 5.

  1. Сначала переменная i равна 0. В заголовке цикла проверяется условие i < 5 , и оно истинно. Тело цикла выполняется. В нем меняется значение i , путем добавления к нему единицы.
  2. Теперь переменная i равна 1. Это меньше пяти, и тело цикла выполняется второй раз. В нем i меняется, ее новое значение 2.
  3. Два меньше пяти. Тело цикла выполняется третий раз. Значение i становится равным трем.
  4. Три меньше пяти. На этой итерации i присваивается 4.
  5. Четыре по прежнему меньше пяти. К i добавляется единица, и теперь ее значение равно пяти.

«Смысловая нагрузка» данного цикла – это последовательное вычитание из переменной total вводимых чисел. Переменная i в данном случае играет только роль счетчика итераций цикла. В других языках программирования для таких случаев предусмотрен цикл for , который так и называется: «цикл со счетчиком». Его преимущество заключается в том, что в теле цикла не надо изменять переменную-счетчик, ее значение меняется автоматически в заголовке for .

В языке Python тоже есть цикл for . Но это не цикл со счетчиком. В Питоне он предназначен для перебора элементов последовательностей и других сложных объектов. Данный цикл и последовательности будут изучены в последующих уроках.

Для while наличие счетчика не обязательно. Представим, что надо вводить числа, пока переменная total больше нуля. Тогда код будет выглядеть так:

total = 100 while total > 0: n = int(input()) total = total - n print("Ресурс исчерпан")

Сколько раз здесь выполнится цикл? Неизвестно, все зависит от вводимых значений. Поэтому у цикла со счетчиком известно количество итераций, а у цикла без счетчика – нет.

Самое главное для цикла while – чтобы в его теле происходили изменения значений переменных, которые проверяются в его заголовке, и чтобы хоть когда-нибудь наступил случай, когда логическое выражение в заголовке возвращает False . Иначе произойдет зацикливание.

Примечание 1. Не обязательно в выражениях total = total — n и i = i + 1 повторять одну и ту же переменную. В Python допустим сокращенный способ записи подобных выражений: total -= n и i += 1 .

Примечание 2. При использовании счетчика он не обязательно должен увеличиваться на единицу, а может изменяться в любую сторону на любое значение. Например, если надо вывести числа кратные пяти от 100 до 0, то изменение счетчика будет таким i = i — 5 , или i -= 5 .

Примечание 3. Для счетчика не обязательно использовать переменную с идентификатором i . Можно назвать переменную-счетчик как угодно. Однако так принято в программировании, что счетчики обозначают именами i и j (иногда одновременно требуются два счетчика).

Практическая работа

  1. Измените последний код из урока так, чтобы переменная total не могла уйти в минус. Например, после предыдущих вычитаний ее значение стало равным 25. Пользователь вводит число 30. Однако программа не выполняет вычитание, а выводит сообщение о недопустимости операции, после чего осуществляет выход из цикла.
  2. Используя цикл while , выведите на экран для числа 2 его степени от 0 до 20. Возведение в степень в Python обозначается как ** . Фрагмент вывода:
. 32 64 128 256 512 1024 .

Примеры решения и дополнительные уроки в pdf-версии курса

X Скрыть Наверх

Python. Введение в программирование

Функция в Python: руководство для начинающих

Мир IT меняется очень быстро, и успевать за ним нелегко. Но если вы хорошо разбираетесь в какой-нибудь технологии, это дает вам определенные преимущества.

Python – широко используемый язык, открывающий множество возможностей. Научиться правильно использовать функции – важный навык для любого Python-разработчика. Поэтому мы и решили написать это руководство по функциям Python для начинающих.

Цель этой статьи – дать вам знания, необходимые для начала работы с функциями в Python.

Итак, давайте начнем!

Зачем нужны функции Python?

Функции управляют входными и выходными данными в компьютерных программах. Языки программирования предназначены прежде всего для работы с данными. А функции — эффективный способ управления этими данными и их преобразованиями.

Модификации данных обычно нужны для получения каких-то результатов, вроде выполнения действий или поиска. Набор операций и инструкций, которые нужны для этого, выполняется из основного кода программы.

Фактически, основной (main) код — это тоже функция, просто очень важная. Все другие функции логически выстраиваются, чтобы выполняться из основного кода.

Но если функция не была объявлена ранее, вам придется сперва ее объявить. Дело в том, что в объявлении перечисляются шаги, которые выполняет функция.

Итак, функции – это не что иное, как задачи, которые хочет выполнять пользователь. И если вы пропишете нужную функциональность один раз и дадите ей имя, вы сможете использовать ее повторно, не превращая свою программу в огромного монстра. Это значительно сокращает количество строк кода и даже упрощает отладку.

Мы скоро поговорим об этом, но первая причина, по которой стоит использовать функцию, – это возможность ее повторного использования.

Функции как средство повторного использования кода

Даже сложные операции вполне могут быть набором нескольких не слишком сложных. А для осуществления каждой небольшой операции можно создать отдельную функцию и вызывать ее по имени. Такой подход сделал современные программы куда более понятными.

Все языки программирования позволяют создавать и использовать функции для решения различных задач. И самое главное — вызывать каждую функцию можно сколько угодно раз.

При этом вам не приходится каждый раз беспокоиться, как код функции встроится в основной код программы. Важно лишь осознавать, что функция выполняется так, как прописано в инструкциях внутри нее.

У машин могут быть разные функции. Калькулятор, вероятно, самый распространенный пример. В нем предусмотрены функции сложения, вычитания, умножения, деления и пр. Все его функции явно предопределены, но он выполняет только те, которые вы выбираете для вызова, нажимая соответствующую кнопку.

Благодаря использованию функций программисты сокращают время на написание кода и отладку, тем самым ускоряя разработку ПО.

Что собой представляет функция в Python?

Функция в Python – классический пример многократного использования кода.

Чтобы обслуживать широкий спектр приложений, от GUI и математических вычислений до веб-разработки и тестирования, интерпретатор Python оснащен многочисленными функциями, готовыми к использованию.

Вы также можете добавлять в свою программу библиотеки или модули, содержащие дополнительные функции. Все, что вам нужно для этого сделать — загрузить необходимые пакеты, в соответствии с их документацией. После этого вы сможете свободно использовать все полезные функции, просто импортировав их в свой код.

Итак, однажды определенная функция может использоваться любое количество раз в любой точке любого вашего кода.

Дело в том, что Python придерживается принципа DRY. Аббревиатура расшифровывается как Don’t Repeat Yourself («Не повторяйся»).

Это один из принципов разработки ПО. Он направлен на замену дубликатов кода абстракциями. Это позволяет избежать избыточности и гарантировать, что абстрагированный код сможет свободно использоваться, не раскрывая внутренних деталей реализации.

Таким образом, все, что вам понадобится для использования функции, это знать ее имя, назначение, аргументы (если она их принимает) и тип ее результата (если она его возвращает).

Определение функции

Функция – это подпрограмма, которая работает с данными и производит некоторый вывод.

Чтобы определить функцию Python, вам нужно будет использовать ключевое слово def перед именем вашей функции. Сразу за именем добавляются круглые скобки, за которыми следует двоеточие.

Python для обозначения блоков использует отступы вместо скобок, чтобы код был более читабельным.

Кроме того, функция может возвращать значение. Это может быть результат выполнения вашей функции или даже выражение или значение, указанное вами после ключевого слова return .

После выполнения оператора return поток программы возвращается в состояние, следующее за вызовом вашей функции, и продолжает выполнение оттуда.

Итак, функция в Python может вызываться в любом месте кода. Для вызова вам нужно будет использовать ее имя и при необходимости передать в нее аргументы в круглых скобках.

Правила присвоения имени функции такие же, как для переменных. Имя должно начинаться или с буквы от A до Z в любом регистре, или с подчеркивания _ . Остальная часть имени может содержать символы подчеркивания, цифры от 0 до 9, любые буквы в верхнем или нижнем регистре.

  • именем функции не может стать зарезервированное ключевое слово
  • важно следить за грамматикой: это повышает читабельность кода.

Кроме того, хорошая практика — называть функцию Python в соответствии с тем, что она делает. Используйте docstring прямо под первой строкой объявления функции. Эта строка документации, объясняющая, что делает функция.

Типы функций в Python

В Python есть много видов функций. И каждый из них по-своему очень важен:

  • встроенные функции
  • рекурсивные функции
  • лямбда-функции
  • пользовательские функции

Давайте подробно рассмотрим эти функции, начиная со встроенных, поскольку их очень легко понять и реализовать.

Встроенные функции Python

Интерпретатор Python имеет ряд функций, которые всегда доступны для использования. Эти функции называются встроенными.

Например, функция print() выводит указанный объект на стандартное устройство вывода (экран) или в файл текстового потока.

В Python 3.6 есть 68 встроенных функций. Давайте рассмотрим несколько наиболее часто используемых.

Функция all()

Метод all() возвращает True , если все элементы в данном итерируемом объекте истинны. В противном случае возвращается False .

Синтаксис метода all() :

all(iterable)

Метод all() принимает единственный параметр — iterable . Это любой итерируемый объект (список, кортеж, словарь и т.д.), который содержит элементы.

# all values true l = [1, 3, 4, 5] print(all(l)) # all values false l = [0, False] print(all(l)) # one false value l = [1, 3, 4, 0] print(all(l)) # one true value l = [0, False, 5] print(all(l)) # empty iterable l = [] print(all(l)) # Output: # True # False # False # False # True
Функция abs()

Метод abs() возвращает абсолютное значение заданного числа. Если число является комплексным, abs() возвращает его величину.

Синтаксис метода abs() :

abs(num)

Метод abs() принимает единственный аргумент — num . Это число, абсолютное значение которого должно быть возвращено. Число может быть целым, с плавающей точкой или комплексным.

# random integer integer = -20 print('Absolute value of -20 is:', abs(integer)) #random floating number floating = -30.33 print('Absolute value of -30.33 is:', abs(floating)) # Output: # Absolute value of -20 is: 20 Absolute value of -30.33 is: 30.33
Функция enumerate()

Метод enumerate() добавляет счетчик к итерируемому объекту и возвращает индексы элементов с их значениями.

enumerate(iterable, start = 0)

Метод enumerate() принимает два параметра:

  • iterable – последовательность, итератор или объекты, поддерживающие итерацию.
  • start (необязательный) – enumerate() начинает отсчет с этого числа. Если start опущен, за начало принимается 0.
grocery = ['bread', 'milk', 'butter'] enumerateGrocery = enumerate(grocery) print(type(enumerateGrocery)) # converting to list print(list(enumerateGrocery)) # changing the default counter enumerateGrocery = enumerate(grocery, 10) print(list(enumerateGrocery)) # Output # # [(0, 'bread'), (1, 'milk'), (2, 'butter')] # [(10, 'bread'), (11, 'milk'), (12, 'butter')]

Также существует множество других встроенных функций, например:

  • ascii() — для вывода символов, отличных от ASCII
  • bin() — для бинарного представления числа
  • dict() — чтобы создать словарь
  • sum() — для вычисления суммы
  • help() — для вызова справочника Python
  • len() — чтобы определить длину итерируемого объекта
  • max() — поиск максимального значения
  • min() — поиск минимального значения
  • pow() — чтобы возвести число в степень.

Рекурсивные функции в Python

Рекурсия – это определение чего-либо в терминах самого себя.

Примером в физическом мире может быть размещение двух параллельных зеркал, обращенных друг к другу. Любой объект между ними будет отражаться рекурсивно.

Мы знаем, что функция в Python может вызывать другие функции. Но функция может вызывать и сама себя. Конструкции такого типа и называются рекурсивными функциями.

Ниже приведен пример рекурсивной функции для поиска факториала целого числа.

Факториал числа – это произведение всех целых чисел от 1 до этого числа. Например, факториал 5 (обозначается как 5!) это 1 * 2 * 3 * 4 * 5 = 120.

# An example of a recursive function to # find the factorial of a number def calc_factorial(x): """This is a recursive function to find the factorial of an integer""" if x == 1: return 1 else: return (x * calc_factorial(x-1)) num = 4 print("The factorial of", num, "is", calc_factorial(num))

В приведенном выше примере calc_factorial() является рекурсивной функцией, потому что она вызывает саму себя.

Когда мы вызываем эту функцию с положительным целым числом, она рекурсивно вызывает себя, уменьшая исходное число.

Наша рекурсия заканчивается, когда число уменьшается до 1. Это называется базовым случаем. Каждая рекурсивная функция должна иметь базовый случай, останавливающий рекурсию, потому что иначе функция будет вызывать себя бесконечно.

  • рекурсивные функции делают код чистым и элегантным
  • с помощью рекурсии сложную задачу можно разбить на более простые подзадачи
  • с помощью рекурсии генерировать последовательность проще, чем с использованием вложенной итерации.
  • иногда трудно понять ее логику
  • рекурсивные вызовы дороги (неэффективны), поскольку занимают много памяти и времени
  • рекурсивные функции сложно отлаживать.

От редакции Pythonist. Больше примеров использования рекурсии можно найти в статье «Примеры программ с использованием рекурсии на языке Python».

Лямбда-функции в Python

В Python можно определить функцию, не давая ей имени. Такая функция называется анонимной.

В то время как обычные функции определяются с помощью ключевого слова def , анонимные функции определяются с помощью ключевого слова lambda .

Лямбда-функция в Python имеет следующий синтаксис:

lambda arguments: expression

Лямбда-функции могут иметь любое количество аргументов, но только одно выражение.

# Program to show the use of lambda functions double = lambda x: x * 2 print(double(5)) # Output # 10

В приведенной выше программе lambda x: x * 2 – это лямбда-функция. Здесь x – аргумент, а x * 2 — выражение, которое вычисляется и возвращается.

У этой функции нет имени. Она возвращает объект функции, которому присвоен идентификатор double . Благодаря этому мы можем вызывать ее, как обычную функцию.

Пользовательские функции Python

Функции, которые поставляются с Python, называются встроенными функциями. Если мы используем функции, написанные другими людьми и выложенные в форме библиотеки, это можно назвать библиотечными функциями.

Все остальные функции, которые мы пишем самостоятельно, относятся к пользовательским функциям. При этом наша пользовательская функция может быть библиотечной функцией для кого-то еще.

Преимущества пользовательских функций

Прежде всего, пользовательские функции помогают разбить большую программу на небольшие сегменты. Это упрощает понимание, поддержку и отладку кода.

Если в программе встречается повторяющийся код, его можно выделить в функцию и выполнять при необходимости путем вызова этой функции.

Кроме того, программисты, работающие над большим проектом, могут разделить рабочую нагрузку, занимаясь разными функциями.

Пример пользовательской функции
def add_numbers(x,y): sum = x + y return sum num1 = 5 num2 = 6 print("The sum is", add_numbers(num1, num2)) # Output: # Enter a number: 2.4 # Enter another number: 6.5 # The sum is 8.9

И в заключение давайте посмотрим, как с помощью функций Python создать простое приложение.

Создаем калькулятор на Python

В этом примере мы создадим простой калькулятор, умеющий складывать, вычитать, умножать или делить числа, введенные пользователем.

# This function adds two numbers def add(x, y): return x + y # This function subtracts two numbers def subtract(x, y): return x - y # This function multiplies two numbers def multiply(x, y): return x * y # This function divides two numbers def divide(x, y): return x / y print("Select operation.") print("1.Add") print("2.Subtract") print("3.Multiply") print("4.Divide") # Take input from the user choice = input("Enter choice(1/2/3/4):") num1 = int(input("Enter first number: ")) num2 = int(input("Enter second number: ")) if choice == '1': print(num1,"+",num2,"=", add(num1,num2)) elif choice == '2': print(num1,"-",num2,"=", subtract(num1,num2)) elif choice == '3': print(num1,"*",num2,"=", multiply(num1,num2)) elif choice == '4': print(num1,"/",num2,"=", divide(num1,num2)) else: print("Invalid input")

В результате получим следующее:

Output Select operation. 1.Add 2.Subtract 3.Multiply 4.Divide Enter choice(1/2/3/4): 3 Enter first number: 15 Enter second number: 14 15 * 14 = 210

Заключение

Надеемся, вам понравилось это руководство по функциям. Теперь вы знаете, что из себя представляет функция в Python, какие функции бывают, как их писать и как ими пользоваться. Создание собственных функций поможет вам удобно добавлять нужный функционал в свои программы.

Успехов в дальнейшем освоении Python!

Как вычислить математическое выражение из строки

Вот так я сделал операцию сложения. Пользователь пишет в чат команду !число 5+5 и бот отвечает 10. Но если пользователь напишет там — или * или /, то программа выдаст ошибку, как можно сделать это лучше, что бы пользователь мог сделать любую арифметическую операцию?

Отслеживать
48.9k 17 17 золотых знаков 56 56 серебряных знаков 100 100 бронзовых знаков
задан 15 авг 2020 в 18:02
145 4 4 серебряных знака 14 14 бронзовых знаков

Я немного углубился в ваш код и у меня возник вопрос , а работает ли у вас сложение с двузначными и трёхзначными числами?

15 авг 2020 в 18:13
Да, всё работает
15 авг 2020 в 18:16
Можно регуляркой найти нужные подстроки
15 авг 2020 в 18:36
pip install numexpr -> numexpr.evaluate(expr)
16 авг 2020 в 9:26

3 ответа 3

Сортировка: Сброс на вариант по умолчанию

Калькуляторы — вещь весёлая, заманчиво «простая», и, на проверку, очень глубокая. По сути — первый шаг в мир компиляторов и парсеров.

Есть много вариантов, как распарсить выражение и вычислить его, много разных грамматик и видов парсеров (LL, LR, PEG, etc.).

Этот процесс обычно состоит из нескольких этапов:

  1. Разбор на лексемы
  2. Построение дерева парсинга
  3. Построение абстрактного синтаксического дерева (из кусков текста выражения)
  4. Построение конкретного синтаксического дерева (из самописных объектов операций и числовых объектов)
  5. Вычисление, обходом конкретного синтаксического дерева

Написание и отладка кода, реализующего этот процесс, могут занять приличное время, но он этого зависит, как гибко ты сможешь расширять грамматику.

Однако, если ограничиваться простыми операциями, тебе не нужен полноценный парсинг — достаточно будет обойтись «упрощенным» вариантом, в котором смешаны сразу и парсинг, и построение дерева, и вычисление. Такой подход усложняет процесс расширения грамматики, однако не перегружает код лишними описаниями и позволяет, написав несколько функций, реализовать вычисление выражений.

Для написания примера такого упрощенного парсера-калькулятора создадим простую грамматику и напишем для неё функции-парсеры.

# num -> /^[+-]?\d+(\.\d+)?/ # group -> ( term ) # value -> num | group # mul -> num [*/] mul # mul -> num # sum -> mul [+-] sum # sum -> mul # term -> sum import re def num(expr): expr = expr.lstrip() res = re.match("^[+-]?\d(\.\d+)?", expr) if res: return float(res.group(0)), expr[res.end():] else: return None, expr def value(expr): res, rest = num(expr) if res != None: return res, rest res, rest = grouping(expr) return res, rest def grouping(expr): expr = expr.lstrip() rest = "" if expr[0] == "(": rest = expr[1:] else: return None, expr numb, rest = term(rest) if rest[0] != ")": return None, expr return numb, rest[1:] def mul_oper(expr): expr = expr.lstrip() res = re.match("[*/]", expr) if res: return res.group(0), expr[res.end():] else: return None, expr def mul(expr): numb1, rest1 = value(expr) if numb1 == None: return None, expr op, rest2 = mul_oper(rest1) if op == None: return numb1, rest1 numb2, rest2 = mul(rest2) if op == "*": return numb1 * numb2, rest2 if op == "/": return numb1 / numb2, rest2 return None, expr def sum_oper(expr): expr = expr.lstrip() res = re.match("[+-]", expr) if res: return res.group(0), expr[res.end():] else: return None, expr def sum(expr): numb1, rest1 = mul(expr) if numb1 == None: return None, expr op, rest2 = sum_oper(rest1) if op == None: return numb1, rest1 numb2, rest2 = sum(rest2) if op == "+": return numb1 + numb2, rest2 if op == "-": return numb1 - numb2, rest2 return None, expr def term(expr): return sum(expr) print(term("(2 + 2) * 2")) 

Здесь функция term() занимается разбором выражения целиком, функции sum() и mul() — разбором выражений сложения/вычитания и умножения/деления с учетом приоритета, функция grouping() занимается разбором выражений в скобках, а num() — разбором чисел. Это урезанная реализация PEG.

После прохождения каждой функции изменяется не только получаемый результат, но и состояние парсера (переменная rest ). В случае, если одна из ветвей парсинга провалилась (не был встречен ожидаемый символ), мы по цепочке подымимся до ближайшей альтернативы (backtracking). Это можно увидеть в работе функции value() , а так же (хоть и скрыто) в функциях mul() и sum() .

Можно написать и чуть более прокаченный вариант с чуть большим погружением в PEG. Отличия будут заключаться в следующем:

  1. Будут добавлены новые функции для построения грамматики: две терминальные и три нетерминальные (комбинаторы, их видов больше, но нам хватит и трёх).
  2. Правила, написанные «вручную» будут переписаны как комбинация более простых правил
  3. Логика выбора пути разбора будет перенесена в комбинаторы
  1. Токен — строгое совпадение набора символов — token()
  2. Регулярное выражение — совпадение по шаблону — rexpr()
  1. Последовательность (или конкатенация) — serial()
  2. Альтернатива — alternative()
  3. Optional — независимо от удачного парсинга возвращает удачу (удача или неудача отличается состоянием переменной rest ) — optional()
import re def GN(func, *args): def exec(text: str): return func(text, *args) return exec def token(text: str, token_text: str): if text.startswith(token_text): return token_text, text[len(token_text):] return None, text def rexpr(text: str, regex: str): text = text.lstrip() res = re.match(regex, text) if res: return res.group(0), text[res.end():] else: return None, text def serial(text, *funcs): res, rest = [], text for func in funcs: resd, restd = func(rest) if resd is None: return None, text res.append(resd) rest = restd if len(res) == 0: return None, text return res, rest def alternative(text, *funcs): for func in funcs: res, rest = func(text) if res is not None: return res, rest return None, text def optional(text, func): res, rest = func(text) return [res], rest def num(expr): res, rest = rexpr(expr, r"^[+-]?\d(\.\d+)?") if res is not None: return float(res), rest return res, rest def value(expr): sign = GN(rexpr, r"[+-]") maybe_sign = GN(optional, sign) val = GN(alternative, num, grouping) res, rest = serial(expr, maybe_sign, val) if res is None: return None, expr numb = res[1] if res[0][0] == "-": return -numb, rest return numb, rest def grouping(expr): opened_bracket = GN(token, "(") # token(text, "(") closed_bracket = GN(token, ")") # token(text, ")") res, rest = serial(expr, opened_bracket, term, closed_bracket) if res is None: return None, expr return res[1], rest def mul(expr): full_expr = GN(serial, value, GN(rexpr, r"[*/]"), mul) res, rest = alternative(expr, full_expr, value) if res is None: return None, expr if isinstance(res, float): return res, rest numb1 = res[0] op = res[1] numb2 = res[2] if op == "*": return numb1 * numb2, rest if op == "/": return numb1 / numb2, rest return None, expr def sum(expr): full_expr = GN(serial, mul, GN(rexpr, r"[+-]"), sum) res, rest = alternative(expr, full_expr, mul) if res is None: return None, expr if isinstance(res, float): return res, rest numb1 = res[0] op = res[1] numb2 = res[2] if op == "+": return numb1 + numb2, rest if op == "-": return numb1 - numb2, rest return None, expr def term(expr): return sum(expr) print(term("2 + 2 * 2")) 

Для того, чтобы комбинаторы работали как надо, необходим общий интерфейс. При написании в ООП стиле достаточно создать для каждого объекта правила метод execute(text: str) . Однако для функций такой интерфейс необходимо создать отдельно: каждая функция помимо текста принимает и другие параметры, такие как, например, правила для rexpr или функции для serial.

Функция GN(rule, \*args) создаёт такую обёртку, возвращая функцию text: str -> Union[Tuple[None, str], Tuple[float, str]]

Калькулятор на python

Здравствуйте, в предыдущей статье я показывал как сделать игру на python, а сейчас мы посмотри как сделать простой калькулятор на python tkinter.

Создаём окно 485 на 550. Размеры не важны, мне понравились такие. Так же указываем, что окно не будет изменяться.

from tkinter import * class Main(Frame): def __init__(self, root): super(Main, self).__init__(root) self.build() def build(self): pass def logicalc(self, operation): pass def update(): pass if __name__ == '__main__': root = Tk() root["bg"] = "#000" root.geometry("485x550+200+200") root.title("Калькулятор") root.resizable(False, False) app = Main(root) app.pack() root.mainloop() 

Отлично, идём дальше.

Делаем кнопочки

В методе build создаём такой список:

btns = [ "C", "DEL", "*", "=", "1", "2", "3", "/", "4", "5", "6", "+", "7", "8", "9", "-", "+/-", "0", "%", "X^2" ] 

Он отвечает за все кнопки, отображающиеся у нас в окне.

Мы создали список, теперь проходимся циклом и отображаем эти кнопки. Для этого в том же методе пишем следующее:

x = 10 y = 140 for bt in btns: com = lambda x=bt: self.logicalc(x) Button(text=bt, bg="#FFF", font=("Times New Roman", 15), command=com).place(x=x, y=y, width=115, height=79) x += 117 if x > 400: x = 10 y += 81 

Замечательно, у нас есть кнопочки. Добавляем надпись с выводом результата. Я хочу что бы текст был слева, следовательно, аттрибутов выравнивания текста писать не нужно.

self.formula = "0" self.lbl = Label(text=self.formula, font=("Times New Roman", 21, "bold"), bg="#000", foreground="#FFF") self.lbl.place(x=11, y=50) 

Пишем логику

def logicalc(self, operation): if operation == "C": self.formula = "" elif operation == "DEL": self.formula = self.formula[0:-1] elif operation == "X^2": self.formula = str((eval(self.formula))**2) elif operation == "=": self.formula = str(eval(self.formula)) else: if self.formula == "0": self.formula = "" self.formula += operation self.update() def update(self): if self.formula == "": self.formula = "0" self.lbl.configure(text=self.formula) 

Так, как у нас нет ввода с клавиатуры, мы можем позволить себе сделать так, просто проверить на спец. кнопки (C, DEL, =) и в остальных случаях просто добавить это к формуле.

У этого калькулятора множество недочетов, но мы и не стремились сделать его идеальным.

Прошу прощения за ошибки в статье. Пишите, я исправлюсь.

Полный код моей версии калькулятора:

from tkinter import * class Main(Frame): def __init__(self, root): super(Main, self).__init__(root) self.build() def build(self): self.formula = "0" self.lbl = Label(text=self.formula, font=("Times New Roman", 21, "bold"), bg="#000", foreground="#FFF") self.lbl.place(x=11, y=50) btns = [ "C", "DEL", "*", "=", "1", "2", "3", "/", "4", "5", "6", "+", "7", "8", "9", "-", "(", "0", ")", "X^2" ] x = 10 y = 140 for bt in btns: com = lambda x=bt: self.logicalc(x) Button(text=bt, bg="#FFF", font=("Times New Roman", 15), command=com).place(x=x, y=y, width=115, height=79) x += 117 if x > 400: x = 10 y += 81 def logicalc(self, operation): if operation == "C": self.formula = "" elif operation == "DEL": self.formula = self.formula[0:-1] elif operation == "X^2": self.formula = str((eval(self.formula))**2) elif operation == "=": self.formula = str(eval(self.formula)) else: if self.formula == "0": self.formula = "" self.formula += operation self.update() def update(self): if self.formula == "": self.formula = "0" self.lbl.configure(text=self.formula) if __name__ == '__main__': root = Tk() root["bg"] = "#000" root.geometry("485x550+200+200") root.title("Калькулятор") root.resizable(False, False) app = Main(root) app.pack() root.mainloop() 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *