
- •Предисловие
- •Глава 1 Введение в Пролог
- •Глава 2 Синтаксис и унификация
- •2.1. Синтаксис термы
- •Константы
- •Переменные
- •Область действия переменных
- •Сложные термы, или структуры
- •Синтаксис операторов
- •Синтаксис списков
- •Синтаксис строк
- •Утверждения
- •Запросы
- •Ввод программ
- •2.2 Унификация
- •Глава 3 Арифметические выражения
- •3.1. Введение
- •3.2. Арифметические выражения
- •3.3. Арифметические операторы
- •3.4. Вычисление арифметических выражений
- •3.5. Сравнение результатов арифметических выражений
- •Глава 4 Рекурсия
- •4.1. Стратегия «разделяй и властвуй»
- •Пример 4.1.1
- •Фаза разбиения
- •Фаза решения задачи
- •4.2. Восходящая стратегия
- •4.3. Рекурсия и эффективность
- •Пример 4.3.2
- •Глава 5 Структуры данных
- •5.1. Списки списковая форма записи
- •А на запрос
- •Некоторые стандартные целевые утверждения для обработки списков
- •5.2. Бинарные деревья представление бинарных деревьев
- •Бинарное дерево на рис.5.2.1 имеет левое поддерево
- •Представление множеств с помощью бинарных деревьев
- •Будем называть линейным представление такого вида, как на рис.5.2.3, и сбалансированным - такое, как на рис.5.2.2.
- •Левому поддереву соответствует отсортированный список
- •Глава 6 Операторы
- •6.1. Операторы и структуры синтаксис операторов
- •Свойства операторов
- •6.2. Позиция операторов
- •6.3. Приоритет операторов
- •6.4. Ассоциативность операторов
- •6.5. Спецификаторы
- •6.6. Операторы объявления
- •6.7. Пример: вычисление многочленов
- •6.8. Системные операторы
- •Глава 7 Механизм возврата и процедурная семантика
- •7.1. Механизм возврата
- •7.2. Пример: задача поиска пути в лабиринте
- •Целевое утверждение не удается согласовать с первым утверждением
- •Если мы введем
- •Если затем мы введем
- •7.3. Обработка фактов с помощью механизма возврата
- •7.4. Предикат repeat
- •7.5. Декларативная и процедурная семантики
- •Характеристики процедуры
- •7.6. Вопросы эффективности
- •Глава 8 Отсечение
- •8.1. Почему используют отсечение?
- •8.2. Использование отсечения
- •Выдав запрос
- •Обратившись к системе с запросом
- •8.3. Ловушки отсечения
- •Глава 9 Встроенные предикаты
- •9.1. Встроенные предикаты
- •9.2. Обновление базы данных Пролога
- •Добавление и удаление утверждений
- •Считывание утверждений в базу данных
- •Предикаты сохранения и восстановления
- •9.3. Особенности ввода и вывода чтение символов
- •Запись символов
- •Считывание термов Предикаты
- •Запись термов
- •9.4. Обработка файлов
- •Редактирование программ на прологе
- •Распечатка предикатов
- •Глава 10 Модули. Пример разработки системы
- •10.1. Модули
- •Пример 10.1.1
- •10.2. Пример разработки системы: деревья решений
- •Описание дерева решений
- •Разработка системы
- •Целевое утверждение
- •Глава 11 Отладка
- •11.1. Трассировка
- •Включение и выключение механизма трассировки
- •Необязательные параметры трассировки
- •Предикаты трассировки
- •Режимы трассировки
- •11.2. Установка контрольных точек
- •Возможные действия в контрольной точке
- •Включение и выключение режима контрольных точек
- •Необязательные параметры режима контрольных точек
- •Предикаты для работы с контрольными точками
- •Режимы, связанные с контрольными точками
- •Основные отладочные предикаты
- •Статистическая информация
5.2. Бинарные деревья представление бинарных деревьев
Бинарное дерево определяется рекурсивно как имеющее левое поддерево, корень и правое поддерево. Левое и правое поддеревья сами являются бинарными деревьями. На рис.5.2.1 показан пример бинарного дерева.
Рис.5.2.1. Бинарное дерево
Такие деревья можно представить термами вида
бд(Лд,к,Пд),
где Лд - левое поддерево, К - корень, а Пд - правое поддерево. Для обозначения пустого бинарного дерева будем использовать атом nil.
Бинарное дерево на рис.5.2.1 имеет левое поддерево
бд(бд(nil,d,nil),b,бд(nil,е,nil))
правое поддерево
бд(nil,c,nil)
и записывается целиком как
бд(бд(бд(nil,d,nil),b,бд(nil,e,nil)),
а,
бд(nil,c,nil)).
Представление множеств с помощью бинарных деревьев
Описание множеств в виде списков позволяет использовать для множеств целевое утверждение принадлежит, определенное ранее для списков.
Однако для множеств, состоящих из большого числа элементов, списковые целевые утверждения становятся неэффективными. Рассмотрим, например, как целевое утверждение принадлежит (см. разд. 5.1.2) позволяет моделировать принадлежность множеству. Пусть L - список, описывающий множество из первых 1024 натуральных чисел. Тогда при ответе на запрос
?- принадлежит(3000,L).
Прологу придется проверить все 1024 числа, прежде чем заключить, что такого числа нет:
нет
Представление множества бинарным деревом позволяет добиться лучшего результата. При этом бинарное дерево должно быть упорядочено таким образом, чтобы любой элемент в левом поддереве был меньше, чем значение корня, а любой элемент в правом поддереве - больше. Поскольку мы определили поддерево как бинарное дерево, такое упорядочение применяется по всем поддеревьям. На рис.5.2.2 приведен пример упорядоченного бинарного дерева.
Дерево на рис.5.2.1 является неупорядоченным.
Рис.5.2.2. Упорядоченное бинарное дерево
Обратите внимание, что упорядочение приводит не к единственному варианту представления множества с помощью дерева. Например, на рис.5.2.3 изображено то же множество, что и на рис.5.2.2.
Будем называть линейным представление такого вида, как на рис.5.2.3, и сбалансированным - такое, как на рис.5.2.2.
Рис.5.2.3. Линейное представление
Моделирование принадлежности множеству. Имея множество, описанное бинарным деревом, мы можем моделировать принадлежность множеству с помощью целевого утверждения принадлежит_дереву. При этом используется оператор @ <, выражающий отношение «меньше, чем», и оператор @ >, выражающий отношение «больше, чем».
/* Граничное условие: Х принадлежит
/* дереву, если Х является корнем.
принадлежит_дереву(Х,бд(Лд,Х,Пд)).
/* Рекурсивные условия
/* Х принадлежит дереву, если Х больше
/* значения корня и находится в правом
/* поддереве:
принадлежит_дереву(Х,бд(Лд,Y,Пд)) :-
X@Y,
принадлежит_дереву(Х,Пд).
/* Х принадлежит дереву, если Х меньше
/* значения корня и находится в левом
/* поддереве:
принадлежит_дереву(Х,бд(Лд,Y,Пд)) :-
X@Y,
принадлежит_дереву(Х,Лд).
Если множество из первых 1024 чисел описать с помощью сбалансированного бинарного дерева Т, то при ответе на запрос
?- принадлежит_дереву(3000,Т).
Пролог сравнит число 3000 не более чем с 11 элементами множества, прежде чем ответит:
нет
Конечно, если Т имеет линейное представление, то потребуется сравнение 3000 с 1024 элементами множества.
Построение бинарного дерева. Задача создания упорядоченного бинарного дерева при добавлении элемента Х к другому упорядоченному бинарному дереву формулируется следующим образом:
Граничное условие:
Добавление Х к nil дает бд(nil,Х,nil).
Рекурсивные условия:
При добавлении Х к бд(Лд,К,Пд) нужно рассмотреть два случая, чтобы быть уверенным, что результирующее дерево будет упорядоченным.
1. Х меньше, чем К. В этом случае нужно добавить Х к Лд, чтобы получить левое поддерево. Правое поддерево равно Пд, а значение корня результирующего дерева равно К.
2. Х больше, чем К. В таком случае нужно добавить Х к Пд, чтобы получить правое поддерево. Левое поддерево равно Лд, а значение корня - К.
Такой формулировке задачи соответствует программа:
/* Граничное условие:
включ_бд(nil,Х,бд(nil,Х,nil)).
/* Рекурсивные условия:
/* (1)
включ_бд(бд(Лд,К,Пд),Х,бд(Лднов,К,Пд)) :-
Х@К,
включ_бд(Лд,Х,Лднон).
/*(2)
включ_бд(бд(Лд,К,Пд),Х,бд(Лд,К,Пднов)) :-
Х@К,
включ_бд(Пд,Х,Пднов).
На запрос
?- включ_бд(nil,d,Т1),включ_бд(Т1,а,Т2).
будут получены значения
T1=бд(nil,d,nil)
T2=бд(бд(nil,a,nil),d,nil)
Процедуру включ_бд() можно использовать для построения упорядоченного дерева из списка:
/* Граничное условие:
список_в_дерево([],nil).
/* Рекурсивное условие:
список_в_дерево([Н | Т],Бд) :-
список_в_дерево(Т,Бд2),
включ_бд(Н,Бд2,Бд).
Заметим, что включ_бд не обеспечивает построения сбалансированного дерева. Однако существуют алгоритмы, гарантирующие такое построение [Horowitz E. Sahni S., 1976, р.442].
Создание отсортированного списка. Для построения отсортированного списка элементов воспользуемся упорядоченным бинарным деревом.
Граничное условие:
Пустое бинарное дерево (nil) приводит к пустому списку [].
Рекурсивное условие:
Отсортированный список для упорядоченного бинарного дерева бд(Лд,К,Пд), где Лд имеет отсортированный список СЛ, а Пд - отсортированный список СП, получается присоединением [К | СП] к СЛ.
Рассмотрим, например, упорядоченное бинарное дерево
бд(бд(бд(nil,alice,nil),fred,бд(nil,graham,nil)),
jim,бд(nil,rау,nil))