
- •Введение
- •1. Математика и современная информатика
- •2. Алгоритмы перевода высказываний с естественного языка на язык математики
- •3. Алгоритм, его свойства, типы и способы записи
- •4. Информация. Формы ее представления, виды и свойства
- •5. Информационные процессы
- •Классификация информационных процессов
- •Когнитивные информационные процессы
- •6. Аналоговая информация
- •7. Дискретная информация
- •8. Количество информации, единицы измерения информации
- •9. Высказывательные логические связки
- •Алгебраические и функциональные языки
- •Классификация функциональных языков
- •Определение атд
- •Синтаксически-ориентированное конструирование
- •Примеры описания атд
- •Атд в языке программирования Haskell
- •Общий вид определения атд в языке Haskell
- •Сопоставление с образцом
- •Классификация атд
- •Атд в других языках программирования
- •11. Системы счисления, используемые в вычислительной технике
- •12. Семантические категории. Логические структуры
- •13. Двоичное кодирование информации
- •Двоичное кодирование символьной (текстовой) информации
- •18. Теория информации и кодирования
- •19. Система кодирования
- •20. Импликация - логическая константа
- •25. Правило де Моргана
- •26. Двоичная система счисления. Операции в двоичной системе счисления
- •27. Восьмеричная система счисления. Операции в восьмеричной системе счисления
- •28. Кванторы общности и существования
- •29. Составные формулы
- •30. Порядок выполнения логических операций
- •Порядок выполнения логических операций в сложном логическом выражении:
- •31. Свойства логических операций
- •6. Законы поглощения:
- •7. Другие (1):
- •35. Закон исключения третьего
- •36. Кодирование изображений
- •37. Кодирование звуков
- •38. Классификационное кодирование
- •39. Кодирование текста
- •Способы кодирования информации.
- •Кодирование символьной (текстовой) информации.
- •Кодирование числовой информации.
- •Кодирование графической информации.
- •Кодирование звуковой информации.
Синтаксически-ориентированное конструирование
Как было показано выше, при создании АТД используются две операции: декартово произведение и размеченное объединение. Эти операции вместе с понятиями теории типов были взяты за основу так называемого синтаксически-ориентированного подхода к конструированию типов, предложенного Чарльзом Энтони Хоаром [1]. Дополнительно о типах в языках программирования можно прочесть в [5].
Данный подход предлагает более удобную нотацию для представления типов, нежели формальные математические записи в теоретико-множественной нотации. В данной нотации типы именуются словами английского языка, начинающимися с заглавной буквы, причём конкретные типы имеют вполне конкретные названия: List, Tree и т. д., а переменные типов (то есть такие обозначения, вместо которых можно подставлять произвольный тип) — просто буквы с начала алфавита, возможно с индексами: A, B, C1, Cn и т. п.
Также в качестве ключевых слов в нотации Ч. Хоара используются слова constructors, selectors, parts и predicates (а также эти же слова в форме единственного числа). Данные ключевые слова используются для ввода наименований отдельных «элементов» АТД. Под «элементами» АТД понимаются различные сущности в составе типа — отдельные размеченные множества и типы, упакованные в контейнеры.
Конструкторы (constructors) — это наименования функций, создающих декартовы произведения из состава АТД.
Селекторы (selectors) — это специальные утилитарные функции, обеспечивающие получение отдельных значений из декартовых произведений.
Части (parts) — наименования отдельных канонических множеств размеченного объединения.
Предикаты (predicates) — функции, позволяющие идентифицировать принадлежность заданного значения конкретному множеству из состава размеченного объединения.
Далее в настоящем разделе каждый из этих элементов описывается более подробно.
Наконец, два символа, (+) и (×), используются для записи определений типов. Знак (+) обозначает размеченное объединение, а знак (×) используется для обозначения декартова произведения.
В качестве примера определения АТД в данной нотации можно привести классическое определение АТД «список элементов типа A»: List(A) = NIL + (A × List(A)) nil, prefix = constructors List(A) head, tail = selectors List(A) NIL, nonNIL = parts List(A) null, nonNull = predicates List(A)
Здесь A — произвольный тип данных, так называемая переменная типов, вместо которой в конкретных случаях можно подставлять любой необходимый тип. Например, если необходимо иметь список целых чисел, то достаточно подставить Int вместо всех вхождений символа A.
На представленном примере можно пояснить основные понятия нотации синтаксически-ориентированного конструирования. Первая строка определения описывает сам тип List(A). Список — это размеченное объединение пустого списка NIL и декартова произведения элемента типа A со списком таких же элементов. Значением типа List(A) может быть либо пустой список, либо непустой, который есть пара (декартово произведение двух множеств), первым элементом которой является значение обёртываемого типа, а вторым — список (в том числе и пустой). Это значит, что «в конце» каждого конечного списка должен находиться пустой список как базис рекурсии.
Следующие четыре строки определяют «элементы» АТД «список». Первая из них определяет два конструктора, каждый из которых соответствует одной из частей размеченного объединения. Конструктор nil создаёт пустой список NIL, конструктор prefix создаёт непустой список соответственно. Этот конструктор принимает на вход значение заданного типа и список, а возвращает пару, первым элементом которой является указанное значение, вторым — список. Следовательно, типы конструкторов можно определить так8: #nil = List(A) #prefix = A → (List(A) → List(A))
Таким образом, тип конструктора типа Ai, являющегося декартовым произведением типов Ai1, Ai2, … × Ain, определяется формулой:
#constructor Ai = Ai1 → (Ai2 → … (Ain → A) … ), |
то есть конструктор одного декартова произведения принимает на вход значения «оборачиваемых» типов (тех, которые помещаются в контейнер), а возвращает значение целевого, своего АТД9. Все части АТД, каждая из которых есть декартово произведение, имеют по одному конструктору. Сам АТД является в таком случае размеченным объединением частей, а части обозначаются ключевым словом parts.
Для всех конструкторов, которые создают «контейнеры», имеются так называемые селекторы. Селектор — это функция, которая возвращает одно определённое значение из контейнера. В случае типа List(A) селекторы есть только у части nonNIL (непустой список). Первый селектор head возвращает голову списка, то есть значение типа A, а второй tail — хвост списка, то есть второй элемент пары в декартовом произведении. Типы селекторов можно понять по их назначению: #head = List(A) → A #tail = List(A) → List(A)
Другими словами, каждый селектор имеет тип вида A → Aik, и такой селектор принимает на вход значение типа АТД, а возвращает заданное значение из контейнера. Селекторы имеют место только для декартовых произведений, а для каждой части типа имеется столько селекторов, сколько типов упаковывается в соответствующий контейнер. Для каждой части типа верно следующее равенство, называемое «аксиомой тектоничности»10:
∀ x ∈ Ai : constructor Ai (si1 x) (si2 x) … (sini x) = x, |
где si1, si2, … sini — селекторы соответствующих компонентов декартова произведения.
Уже упомянутые части типа — это множества, включённые в АТД посредством размеченного объединения. Для АТД определяются предикаты, при помощи которых можно выявить, к какому конкретно множеству в рамках размеченного объединения относится значение. Наличие таких предикатов — одно из свойств размеченности объединения. Соответственно, сколько в АТД частей, столько и предикатов. Части задаются ключевым словом parts, предикаты — predicates. Для предикатов верна следующая аксиома:
(x ∈ Ai) ⇒ (Pi x = 1) & (∀ j ≠ i : Pj x = 0). |
Наличие такой аксиомы необходимо для того, чтобы для произвольного значения АТД можно было выявить ту конкретную часть, к которой это значение принадлежит. Далее можно будет применять селекторы конкретной части (применение селекторов к значению из другой части приведёт к ошибке согласования типов — необходимо помнить о типе селекторов). Соответственно, при реализации в языках программирования такие предикаты позволяют использовать механизм сопоставления с образцом (подробно рассказывается ниже в разделе про язык Haskell).
Нотация Ч. Э. Хоара также позволяет представлять АТД в виде диаграмм, на которых представлено древовидное описание структуры АТД. Такие деревья состоят из двух или трёх уровней. На первом уровне изображается вершина АТД с его наименованием. На втором уровне перечисляются части типа. Если часть представляет собой декартово произведение, то для данной части на третьем уровне перечисляются типы компонентов части. Рёбра дерева, ведущие с первого на второй уровень, помечаются наименованиями предикатов. Соответственно, рёбра, ведущие со второго на третий уровень, помечаются наименованиями селекторов.
Произвольный АТД выглядит так, как показано на рис. 2.
Рис. 2: Древовидное представление АТД |
В качестве примера представления АТД в виде дерева можно привести диаграмму для типа List(A), показанную на рис. 3. На приведённой диаграмме пунктирной линией показано рекурсивное включение типа List(A) в качестве одного из своих компонентов.
Рис. 3: Древовидное представление типа List(A) |