1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6

# конспект · шаг 6 из 6

Конспект: абстрактный тип и реализация

выжимка - её можно унести в заметки

Понятия

Абстрактный тип данных (АТД) - описание структуры набором операций и их контрактом (что каждая делает), без единого слова о хранении в памяти. Пример: «список» - это длина, взять(i), добавить, вставить, удалить плюс обещания их поведения.

Реализация - конкретное представление данных в памяти плюс код операций под это представление. Одной абстракции отвечает МНОГО реализаций.

Стоимость операции - часть контракта наравне с поведением. Разные реализации одного АТД дают одинаковые ответы, но разную цену операций.

Таблица «операция × реализация» - способ выбрать реализацию: строки - операции, столбцы - реализации, в клетках - порядок роста. Берут ту, где ЧАСТЫЕ операции дёшевы.

Инвариант представления - условие про внутреннее состояние, истинное между операциями: каждая на него опирается и каждая его восстанавливает. На нём держится и корректность, и обещанная цена.

Что унести

Абстракцию и реализацию разделяют, чтобы код-пользователь опирался на операции, а не на устройство: тогда начинку можно поменять, ничего не сломав. На этом стоит вся стандартная библиотека - list, dict, set - интерфейсы, начинка спрятана.

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

Ключевые клетки таблицы:

операция массив связный список множество
взять i-й O(1) O(n) -
вставить в начало O(n) O(1) -
добавить в конец O(1) аморт. O(1) -
принадлежит? (по значению) O(n) O(n) O(1)

Массив силён в индексации, связный список - во вставке с краю, множество - в проверке принадлежности. «Принадлежит?» в цикле по n - это O(n²) со списком и O(n) с множеством: та самая задача главы.

Обещанная цена - следствие инварианта. Отсортированность даёт бинарный поиск O(log n); num ≤ size с удвоением даёт вставку O(1) амортизированно. Нарушишь инвариант извне - рухнут и цена, и правильность, поэтому внутренности прячут (инкапсуляция).

Вид цены пишут в контракт честно: «O(1) амортизированно», «в среднем», «в худшем» - разные обещания. append - не «O(1) всегда»: отдельная вставка при удвоении O(n), гарантирована средняя по серии.

Ловушки джокера

Спутал интерфейс с реализацией. Заложился на «как хранится» (читаю память напрямую) - и код привязан к одной реализации, сменят её, и всё посыпется.

Решил, что раз ответы равны, реализации взаимозаменяемы. Ответ один, цена разная; индексация по связному списку в цикле снова даёт O(n²).

Выбрал структуру по привычке. «Везде беру список» - и проверка «принадлежит?» в цикле даёт квадрат там, где годилось множество.

Полез в приватное представление снаружи. Нарушил инвариант - структура начинает «иногда» отвечать неверно, и поймать это тяжело.

Принял амортизированную цену за гарантию на каждый вызов. Где важен худший ОДИН шаг (реальное время), редкая дорогая операция подведёт - нужна гарантия в худшем случае.

Проверь себя

Чем абстрактный тип отличается от реализации?

Почему две реализации одного АТД дают одинаковые ответы, но выбирают между ними по цене?

Где дешевле «принадлежит?» и где «взять i-й» - и почему?

Что такое инвариант представления и на что он опирает обещанную стоимость?

Почему append пишут «O(1) амортизированно», а не «O(1) всегда»?

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

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