Skip to content

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

Разработка калькулятора при помощи обратной польской записи

Section titled “Разработка калькулятора при помощи обратной польской записи”

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

Основные теоретические положения

Section titled “Основные теоретические положения”

Обратная польская запись (ОПЗ) / Постфиксная нотация

Section titled “Обратная польская запись (ОПЗ) / Постфиксная нотация”

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

НотацияПримерОписание
Инфиксная(2 + 3) * 4Оператор находится между операндами. Требует скобок и правил приоритета.
Постфиксная (ОПЗ)2 3 + 4 *Оператор находится после операндов. Порядок вычисления однозначен.

Преимущества ОПЗ:

  • Устранение скобок: Выражения не требуют скобок, что упрощает парсинг.
  • Простота вычисления: Алгоритм вычисления ОПЗ-выражения очень прост и основан на использовании стека.

Роль структуры данных Стек

Section titled “Роль структуры данных Стек”

Структура данных стек (LIFO — Last In, First Out) является ключевой для работы с ОПЗ. Она используется в двух основных процессах:

  1. Преобразование в ОПЗ (Инфикс -> Постфикс): Стек используется для временного хранения операторов и скобок, чтобы обеспечить правильный порядок их вывода в постфиксное выражение с учетом приоритетов.
  2. Вычисление ОПЗ-выражения: Стек используется для хранения операндов (чисел). Когда встречается оператор, он извлекает необходимое количество операндов из стека, выполняет операцию и помещает результат обратно в стек.

1. Алгоритм преобразования инфиксного выражения в ОПЗ (Алгоритм сортировочной станции)

Section titled “1. Алгоритм преобразования инфиксного выражения в ОПЗ (Алгоритм сортировочной станции)”

Этот алгоритм, известный как Алгоритм сортировочной станции (Shunting-yard algorithm), использует два основных компонента: входную строку (инфиксное выражение), стек для операторов и выходную строку (ОПЗ-выражение).

Детальные шаги алгоритма:

  1. Инициализация: Создать пустой стек для операторов и пустую строку/список для ОПЗ.
  2. Чтение символов: Читать элементы инфиксного выражения слева направо.
  3. Операнд (число): Если элемент — операнд (число), добавить его сразу в ОПЗ-выражение.
  4. Открывающая скобка (: Поместить ее в стек.
  5. Закрывающая скобка ):
    • Извлекать операторы из стека и добавлять их в ОПЗ-выражение до тех пор, пока на вершине стека не будет найдена открывающая скобка.
    • Удалить открывающую скобку из стека, но не добавлять ее в ОПЗ.
    • Если стек опустел, а открывающая скобка не найдена, значит, скобки расставлены неверно.
  6. Оператор:
    • Пока стек не пуст, и на вершине стека находится оператор с большим или равным приоритетом, чем текущий оператор, извлекать оператор из стека и добавлять его в ОПЗ.
    • После этого поместить текущий оператор в стек.
  7. Конец выражения: Когда входное выражение закончилось, извлечь все оставшиеся операторы из стека и добавить их в ОПЗ. Если в стеке остались скобки, значит, они расставлены неверно.

Пример пошагового преобразования: 2 + 3 * 4

Входной символСтек операторовВыход (ОПЗ)Примечание
2[]2Операнд, сразу в выход
+[+]2Оператор, помещаем в стек
3[+]2 3Операнд, сразу в выход
*[+, *]2 3* имеет больший приоритет, чем +, помещаем в стек
4[+, *]2 3 4Операнд, сразу в выход
Конец[+, *]2 3 4 * +Извлекаем оставшиеся операторы: сначала *, потом +
Итог[]2 3 4 * +

2. Алгоритм вычисления ОПЗ-выражения

Section titled “2. Алгоритм вычисления ОПЗ-выражения”

Этот алгоритм использует один стек для операндов.

Детальные шаги алгоритма:

  1. Инициализация: Создать пустой стек для операндов.
  2. Чтение элементов: Читать элементы ОПЗ-выражения слева направо (элементы разделены пробелами).
  3. Операнд (число): Если элемент — операнд, поместить его в стек.
  4. Оператор: Если элемент — оператор (например, +, -, *, /):
    • Извлечь два верхних операнда из стека (первый извлеченный — operand2, второй — operand1).
    • Выполнить операцию: result = operand1 operator operand2.
    • Поместить result обратно в стек.
  5. Конец выражения: Когда выражение закончилось, единственный оставшийся элемент в стеке является результатом вычисления.

Пример пошагового вычисления: 2 3 4 * +

Входной элементСтек операндовОперацияПримечание
2[2]Операнд, помещаем в стек
3[2, 3]Операнд, помещаем в стек
4[2, 3, 4]Операнд, помещаем в стек
*[2, 12]3 * 4 = 12Извлекаем 4 и 3, выполняем операцию, помещаем 12
+[14]2 + 12 = 14Извлекаем 12 и 2, выполняем операцию, помещаем 14
Итог[14]Результат — 14

Пример реализации (C++): Общие принципы

Section titled “Пример реализации (C++): Общие принципы”

Для реализации калькулятора на C++ рекомендуется использовать стандартный контейнер std::stack из библиотеки <stack>.

#include <iostream>
#include <stack>
#include <string>
#include <sstream>
#include <cmath>
#include <map>
// Псевдокод для реализации:
// 1. Функция определения приоритета оператора:
// int get_precedence(char op) { ... }
// 2. Функция преобразования в ОПЗ:
// std::string infix_to_rpn(const std::string& infix) {
// // Используем std::stack<char> для операторов
// // Реализуем логику Алгоритма сортировочной станции
// }
// 3. Функция вычисления ОПЗ:
// double evaluate_rpn(const std::string& rpn_expression) {
// // Используем std::stack<double> для операндов
// // Реализуем логику вычисления ОПЗ
// }
// 4. Основная функция main():
// int main() {
// // Запрашиваем инфиксное выражение
// // Вызываем infix_to_rpn
// // Вызываем evaluate_rpn
// // Выводим результат
// }

Компиляция и запуск программы

Section titled “Компиляция и запуск программы”
Terminal window
g++ calculator.cpp -o calculator
./calculator
  1. Программа должна быть написана на языке C++.
  2. Обязательно использование структуры данных стек для реализации алгоритмов.
  3. Программа должна корректно обрабатывать целые и дробные числа.
  4. Программа должна обрабатывать основные арифметические операции: +, -, *, /.
  5. Программа должна корректно обрабатывать скобки ( и ).
  6. Программа должна выводить промежуточные результаты (ОПЗ-выражение) и конечный результат.

Задания для выполнения

Section titled “Задания для выполнения”

Базовый калькулятор

ЗадачаОписание
1Преобразование в ОПЗРеализуйте функцию, которая принимает инфиксное выражение (строку) и преобразует его в ОПЗ (строку). Поддерживаемые операции: +, -, *, /, скобки.
2Вычисление ОПЗРеализуйте функцию, которая принимает ОПЗ-выражение (строку) и вычисляет его значение, используя стек для операндов.
3ИнтеграцияОбъедините функции: пользователь вводит инфиксное выражение, программа выводит ОПЗ и результат вычисления. Протестируйте на выражениях: (2 + 3) * 4 и 10 / (5 - 3).

  1. Объясните, почему для вычисления ОПЗ-выражения необходим стек.
  2. В чем основное отличие инфиксной, префиксной и постфиксной нотаций?
  3. Опишите принцип работы “Алгоритма сортировочной станции” (Shunting-yard algorithm).
  4. Какие правила приоритета и ассоциативности операторов необходимо учитывать при преобразовании в ОПЗ?
  5. Как в вашем алгоритме обрабатывается унарный минус?

Рекомендуемая литература

Section titled “Рекомендуемая литература”
  1. Ахо А., Сети Р., Ульман Дж. Компиляторы: принципы, технологии и инструментарий. — М.: Вильямс, 2008. (Глава 2.4. Синтаксический анализ выражений)
  2. Вирт Н. Алгоритмы и структуры данных. — М.: ДМК Пресс, 2010. (Глава 4. Стеки и очереди)
  3. Шилдт Г. C++: Полное руководство. — М.: Диалектика, 2018. (Глава о стандартной библиотеке: std::stack)