Ооп в картинках

ФП против ООП

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


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

Но ортогональность не подразумевает взаимного исключения (спросите Джеймса Клерка Максвелла). Вполне можно создать систему, которая использует и динамический полиморфизм и референциальную прозрачность. Это не только возможно, это правильно и хорошо!

Почему эта комбинация хороша? По точно тем же причинам, что оба её компонента! Системы построенные на динамическом полиморфизме хороши, потому что они обладают низкой связностью. Зависимости можно инвертировать и расположить по разные стороны архитектурных границ. Эти системы можно тестировать используя Моки и Фейки и другие виды Тестовых Дублей. Модули можно модифицировать не внося изменения в другие модули. Поэтому такие системы проще изменять и улучшать.

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

Главная мысль тут такая:

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

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

Определение классов в C++

Класс — это пользовательский тип данных (также как и структуры). Т.е. тип данных, который вы создаёте сами. Для этого вы пишете определение класса. Определение класса состоит из заголовка и тела. В заголовке ставится ключевое слов class, затем имя класса (стандартный идентификатор C++). Тело помещается в фигурные скобки. В C++ классы и структуры почти идентичны. В языке C в структурах можно хранить только данные, но в C++ в них можно добавить действия.

class Tank { private: int ammo;

public: void Attack() { ammo -= 1; } };


В C++ ключевые слова struct и class очень близки и могут использоваться взаимозаменяемо. У них есть только одно отличие (об этом ниже). Вот как можно определить такой же класс с помощью struct:

struct Tank { private: int ammo;

public: void Attack() { ammo -= 1; } };

Отличие только первом ключевом слове. В одном из прошлых уроков мы уже обсуждали структуры. что мы видим новое? Ключевые слова private и public — это спецификаторы доступа. Также мы видим, что внутри класса мы можем вставлять определения функций.

Определение класса это чертёж. Оно говорит нам из каких данных состоит класс и какие действия он может совершать. т.е. происходит объединение данных и действий в одной сущности.

Переменные и методы класса

Класс состоит из членов класса (class members). Члены класса могут быть переменными (data members) или методами (function members или methods). Переменные класса могут иметь любой тип данных (включая другие структуры и классы). Методы — это действия, которые может выполнять класс. По сути, это обычные функции.

Все методы класса имеют доступ к переменным класса

Обратите внимание, как мы обращаемся к ammo в методе Attack

Общие задачи по ООП:

Решаются на любом языке

1) Класс Дробное число со знаком (Fractions). Число должно быть представлено двумя полями: целая часть — длинное целое со знаком, дробная часть — беззнаковое короткое целое. Реализовать арифметические операции сложения, вычитания, умножения и операции сравнения. В функции main проверить эти методы.

2) Класс Деньги для работы с денежными суммами. Число должно быть представлено двумя полями: типа long для рублей и типа unsigned char — для копеек. Дробная часть (копейки) при выводе на экран должна быть отделена от целой части запятой. Реализовать сложение, вычитание, деление сумм, деление суммы на дробное число, умножение на дробное число и операции сравнения. В функции main проверить эти методы.

3) Класс Равнобочная трапеция, члены класса: координаты 4-х точек. Предусмотреть в классе конструктор и методы: проверка, является ли фигура равнобочной трапецией; вычисления и вывода сведений о фигуре: длины сторон, периметр, площадь. В функции main продемонстрировать работу с классом: дано N трапеций, найти количество трапеций, у которых площадь больше средней площади.

4)Описать базовый класс Строка. Обязательные поля класса:

указатель pChar хранит адрес динамически выделенной памяти для размещения символов строки; значение типа word хранит длину строки в байтах. Реализовать обязательные методы следующего назначения: -конструктор без параметров; -конструктор, принимающий в качестве параметра строковый литерал; п конструктор, принимающий в качестве параметра символ; -метод получения длины строки; -метод очистки строки (делает строку пустой); -деструктор.

Описать производный от Строка класс Комплексное число.

Строки данного класса должны состоять |из двух полей разделочных символом i. Первое поле задает значение действительной части числа, а второе — значение мнимой. Каждое из полей может содержать только символы десятичных цифр и символы — и +, определяющие знак числа. Символы — или + могут находиться только в первой позиции числа, причем символ + может отсутствовать, в этом случае число считается положительным. Если в составе инициализирующей строки будет встречен любой символ, отличный от допустимых, объект класса Комплексное число должен принимать нулевое значение. Примеры строк: 33il2, -7U00, +5i-21.

Для класса Комплексное_число определить следующие методы: -проверка на равенство; -сложение чисел; -умножение чисел. Написать тестовую программу, которая демонстрирует работу методов базового и производного классов.


5) Описать шаблонный класс List для работы с однонаправленными списками в динамической памяти.

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

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

Какой будет игра

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

Устроена игра будет так:

  1. При запуске вызывается метод InitGame (), в котором будут созданы игровые объекты, предметы и прочее.
  2. После будет вызван метод Update (), который обновляет состояние игры.

Внутри метода Update () должен находиться цикл со следующими действиями:

  1. Отрисовка локации или инвентаря.
  2. Получение нажатой игроком клавиши.
  3. Передача клавиши в контроллер.
  4. Контроллер, в зависимости от нажатой клавиши, будет вызывать методы игровых объектов: например, Move () или Use ().

Для управления игрой мы используем три статических класса-контроллера:

  1. LocationController — перехватывает действия игрока на локации.
  2. InventoryController — перехватывает действия игрока в инвентаре.
  3. GraphicsController — управляет выводом.

Игровые данные (размеры локации, список объектов) находятся в статическом классе Game. Объекты будут реализованы с помощью классов GameObject (базовый), Player и NPC. За расположение и перемещение по локации пусть отвечает класс Position.

Предметы реализуются с помощью классов Item (базовый), Potion и Meal. Если предмет может быть использован, то в нём реализуется интерфейс IUsable.

Задача 5. Думский регламент

Input file: стандартный ввод
Output file: стандартный вывод
Time limit: 2 секунды
Memory limit: 512 мебибайт

В Тридевятом Царстве уже много лет как установилась конституционная монархия. В парламент Тридевятого царства входят 26 партий, обозначаемых строчными буквами английского алфавита от «a» до «z». Заседание парламента в соответствии с регламентом проходит по следующей схеме:

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

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

Входные данные

Первая строка входных данных содержит одно целое число K – количество строк в записи сессии (1 ≤ K ≤ 1000). Каждая строка соответствует одному из двух событий:

  • Add x – партия x внесла на голосование законопроект.
  • Vote x – прошло голосование за документ, внесённый партией x.

Здесь x – строчная буква английского алфавита от «a» до «z», задающая партию.

Вывод

Выведите «Yes», если существует корректный порядок проведения заседания, который мог привести к такой записи, и «No», если ни при каком корректном порядке проведения заседания данная запись появиться не могла.

Примеры

стандартный ввод стандартный вывод
4 Add a Add b Vote a Vote b No
8 Add z Vote z Add x Add y Add x Vote x Vote y Vote x Yes
1 Vote z No

Обратим внимание на следующие факты:

  1. Пустая запись является корректной.
  2. Если две записи являются корректными, то их слияние тоже является корректным. Действительно, получится заседание, в котором к моменту окончания первой записи быди приняты все внесённые законопроекты, а затем заседание продолжилось внесением очередного законопроекта и также корректно завершилось.
  3. Если запись является корректной, её можно «вставить» внутрь обсуждения одного законопроекта и получить корректную запись. Это прямо следует из третьего пункта регламента.

Несложно показать, что любую запись можно получить из пустой с помощью действий 2 или 3. Действительно, пусть у нас есть некая корректная запись. Рассмотрим первый законопроект. Он или был поставлен на голосование последним (тогда был применено третье действие – вся оставшаяся запись была вставлена в обсуждение этого законопроекта), или был поставлен на голосование в какой-то момент раньше. Тогда к моменту после голосования по первому законопроекту все внесённые законопроекты уже «закрыты», то есть часть записи от внесения законопроекта на обсуждение до голосования представляет собой корректную запись. Аналогично оставшаяся часть представляет собой корректную запись (так как к моменту её начала никаких законопроектов на повестке дня не стоит, то стартовые условия выполняются, остальные же требования следуют из корректности первоначальной записи), и в этом случае было применено второе действие. В каждом случае получаем переход к записи меньшей длины; будем продолжать действовать таким образом, пока не дойдём до пустой записи.

Гид по спортивному программированию: обзор соревнований и советы по участию в них

tproger.ru

Тем самым мы показали, что условие задачи эквивалентно правильной скобочной последовательности с 26 типами скобок. Для проверки того, что скобочная последовательность является правильной, лучше всего использовать стек: в начале обсуждения законопроекта мы кладём на стек букву, соответствующую внёсшей его партии, по окончании – сравниваем вершину стека с буквой, соответствующей партии, законопроект которой поставлен на голосование (если не равны – запись некорректна) и снимаем букву со стека. Если в какой-то момент времени была попытка взять вершину пустого стека – запись некорректна. Если по завершении всей записи стек не пуст – какие-то законопроекты не поставлены на голосование и запись некорректна. Во всех остальных случаях запись корректна.


С этим читают